| // Copyright 2012 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <stdint.h> |
| |
| #include <algorithm> |
| #include <iterator> |
| #include <limits> |
| #include <memory> |
| #include <utility> |
| |
| #include "base/base64url.h" |
| #include "base/compiler_specific.h" |
| #include "base/files/file_path.h" |
| #include "base/files/file_util.h" |
| #include "base/files/scoped_temp_dir.h" |
| #include "base/format_macros.h" |
| #include "base/functional/bind.h" |
| #include "base/functional/callback_helpers.h" |
| #include "base/location.h" |
| #include "base/memory/ptr_util.h" |
| #include "base/memory/raw_ptr.h" |
| #include "base/memory/weak_ptr.h" |
| #include "base/path_service.h" |
| #include "base/ranges/algorithm.h" |
| #include "base/run_loop.h" |
| #include "base/strings/escape.h" |
| #include "base/strings/strcat.h" |
| #include "base/strings/string_number_conversions.h" |
| #include "base/strings/string_piece.h" |
| #include "base/strings/string_split.h" |
| #include "base/strings/string_util.h" |
| #include "base/strings/stringprintf.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "base/task/single_thread_task_runner.h" |
| #include "base/test/bind.h" |
| #include "base/test/metrics/histogram_tester.h" |
| #include "base/test/scoped_feature_list.h" |
| #include "base/test/values_test_util.h" |
| #include "base/time/time.h" |
| #include "base/values.h" |
| #include "build/build_config.h" |
| #include "build/buildflag.h" |
| #include "crypto/sha2.h" |
| #include "net/base/chunked_upload_data_stream.h" |
| #include "net/base/directory_listing.h" |
| #include "net/base/elements_upload_data_stream.h" |
| #include "net/base/features.h" |
| #include "net/base/hash_value.h" |
| #include "net/base/ip_address.h" |
| #include "net/base/ip_endpoint.h" |
| #include "net/base/isolation_info.h" |
| #include "net/base/load_flags.h" |
| #include "net/base/load_timing_info.h" |
| #include "net/base/load_timing_info_test_util.h" |
| #include "net/base/net_errors.h" |
| #include "net/base/net_module.h" |
| #include "net/base/proxy_server.h" |
| #include "net/base/proxy_string_util.h" |
| #include "net/base/request_priority.h" |
| #include "net/base/test_completion_callback.h" |
| #include "net/base/transport_info.h" |
| #include "net/base/upload_bytes_element_reader.h" |
| #include "net/base/upload_data_stream.h" |
| #include "net/base/upload_file_element_reader.h" |
| #include "net/base/url_util.h" |
| #include "net/cert/asn1_util.h" |
| #include "net/cert/caching_cert_verifier.h" |
| #include "net/cert/cert_net_fetcher.h" |
| #include "net/cert/cert_verifier.h" |
| #include "net/cert/coalescing_cert_verifier.h" |
| #include "net/cert/crl_set.h" |
| #include "net/cert/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/dns/public/host_resolver_results.h" |
| #include "net/dns/public/secure_dns_policy.h" |
| #include "net/http/http_byte_range.h" |
| #include "net/http/http_cache.h" |
| #include "net/http/http_network_layer.h" |
| #include "net/http/http_network_session.h" |
| #include "net/http/http_request_headers.h" |
| #include "net/http/http_response_headers.h" |
| #include "net/http/http_server_properties.h" |
| #include "net/http/http_transaction_test_util.h" |
| #include "net/http/http_util.h" |
| #include "net/http/transport_security_state.h" |
| #include "net/http/transport_security_state_source.h" |
| #include "net/log/file_net_log_observer.h" |
| #include "net/log/net_log.h" |
| #include "net/log/net_log_event_type.h" |
| #include "net/log/net_log_source.h" |
| #include "net/log/test_net_log.h" |
| #include "net/log/test_net_log_util.h" |
| #include "net/net_buildflags.h" |
| #include "net/proxy_resolution/configured_proxy_resolution_service.h" |
| #include "net/quic/mock_crypto_client_stream_factory.h" |
| #include "net/quic/quic_server_info.h" |
| #include "net/socket/read_buffering_stream_socket.h" |
| #include "net/socket/socket_test_util.h" |
| #include "net/socket/ssl_client_socket.h" |
| #include "net/ssl/client_cert_identity_test_util.h" |
| #include "net/ssl/ssl_connection_status_flags.h" |
| #include "net/ssl/ssl_private_key.h" |
| #include "net/ssl/ssl_server_config.h" |
| #include "net/ssl/test_ssl_config_service.h" |
| #include "net/test/cert_test_util.h" |
| #include "net/test/embedded_test_server/http_request.h" |
| #include "net/test/embedded_test_server/http_response.h" |
| #include "net/test/gtest_util.h" |
| #include "net/test/spawned_test_server/spawned_test_server.h" |
| #include "net/test/test_data_directory.h" |
| #include "net/test/test_with_task_environment.h" |
| #include "net/test/url_request/url_request_failed_job.h" |
| #include "net/test/url_request/url_request_mock_http_job.h" |
| #include "net/traffic_annotation/network_traffic_annotation_test_helper.h" |
| #include "net/url_request/redirect_util.h" |
| #include "net/url_request/referrer_policy.h" |
| #include "net/url_request/static_http_user_agent_settings.h" |
| #include "net/url_request/url_request.h" |
| #include "net/url_request/url_request_context.h" |
| #include "net/url_request/url_request_context_builder.h" |
| #include "net/url_request/url_request_filter.h" |
| #include "net/url_request/url_request_http_job.h" |
| #include "net/url_request/url_request_interceptor.h" |
| #include "net/url_request/url_request_redirect_job.h" |
| #include "net/url_request/url_request_test_job.h" |
| #include "net/url_request/url_request_test_util.h" |
| #include "net/url_request/websocket_handshake_userdata_key.h" |
| #include "testing/gmock/include/gmock/gmock-matchers.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "testing/platform_test.h" |
| #include "third_party/abseil-cpp/absl/types/optional.h" |
| #include "third_party/boringssl/src/include/openssl/ssl.h" |
| #include "url/url_constants.h" |
| #include "url/url_util.h" |
| |
| #if BUILDFLAG(IS_WIN) |
| #include <objbase.h> |
| #include <shlobj.h> |
| #include <windows.h> |
| #include <wrl/client.h> |
| |
| #include "base/win/scoped_com_initializer.h" |
| #endif |
| |
| #if BUILDFLAG(IS_APPLE) |
| #include "base/mac/mac_util.h" |
| #endif |
| |
| #if BUILDFLAG(ENABLE_REPORTING) |
| #include "net/network_error_logging/network_error_logging_service.h" |
| #include "net/network_error_logging/network_error_logging_test_util.h" |
| #endif // BUILDFLAG(ENABLE_REPORTING) |
| |
| #if BUILDFLAG(ENABLE_WEBSOCKETS) |
| #include "net/websockets/websocket_test_util.h" |
| #endif // BUILDFLAG(ENABLE_WEBSOCKETS) |
| |
| using net::test::IsError; |
| using net::test::IsOk; |
| using net::test_server::RegisterDefaultHandlers; |
| using testing::_; |
| using testing::AnyOf; |
| using testing::ElementsAre; |
| using testing::IsEmpty; |
| using testing::Optional; |
| using testing::UnorderedElementsAre; |
| |
| using base::ASCIIToUTF16; |
| using base::Time; |
| using std::string; |
| |
| namespace net { |
| |
| namespace { |
| |
| namespace test_default { |
| #include "net/http/transport_security_state_static_unittest_default.h" |
| } |
| |
| const std::u16string kChrome(u"chrome"); |
| const std::u16string kSecret(u"secret"); |
| const std::u16string kUser(u"user"); |
| |
| const base::FilePath::CharType kTestFilePath[] = |
| FILE_PATH_LITERAL("net/data/url_request_unittest"); |
| |
| // Tests load timing information in the case a fresh connection was used, with |
| // no proxy. |
| void TestLoadTimingNotReused(const LoadTimingInfo& load_timing_info, |
| int connect_timing_flags) { |
| EXPECT_FALSE(load_timing_info.socket_reused); |
| EXPECT_NE(NetLogSource::kInvalidId, load_timing_info.socket_log_id); |
| |
| EXPECT_FALSE(load_timing_info.request_start_time.is_null()); |
| EXPECT_FALSE(load_timing_info.request_start.is_null()); |
| |
| EXPECT_LE(load_timing_info.request_start, |
| load_timing_info.connect_timing.connect_start); |
| ExpectConnectTimingHasTimes(load_timing_info.connect_timing, |
| connect_timing_flags); |
| EXPECT_LE(load_timing_info.connect_timing.connect_end, |
| load_timing_info.send_start); |
| EXPECT_LE(load_timing_info.send_start, load_timing_info.send_end); |
| EXPECT_LE(load_timing_info.send_end, load_timing_info.receive_headers_start); |
| EXPECT_LE(load_timing_info.receive_headers_start, |
| load_timing_info.receive_headers_end); |
| |
| EXPECT_TRUE(load_timing_info.proxy_resolve_start.is_null()); |
| EXPECT_TRUE(load_timing_info.proxy_resolve_end.is_null()); |
| } |
| |
| // Same as above, but with proxy times. |
| void TestLoadTimingNotReusedWithProxy(const LoadTimingInfo& load_timing_info, |
| int connect_timing_flags) { |
| EXPECT_FALSE(load_timing_info.socket_reused); |
| EXPECT_NE(NetLogSource::kInvalidId, load_timing_info.socket_log_id); |
| |
| EXPECT_FALSE(load_timing_info.request_start_time.is_null()); |
| EXPECT_FALSE(load_timing_info.request_start.is_null()); |
| |
| EXPECT_LE(load_timing_info.request_start, |
| load_timing_info.proxy_resolve_start); |
| EXPECT_LE(load_timing_info.proxy_resolve_start, |
| load_timing_info.proxy_resolve_end); |
| EXPECT_LE(load_timing_info.proxy_resolve_end, |
| load_timing_info.connect_timing.connect_start); |
| ExpectConnectTimingHasTimes(load_timing_info.connect_timing, |
| connect_timing_flags); |
| EXPECT_LE(load_timing_info.connect_timing.connect_end, |
| load_timing_info.send_start); |
| EXPECT_LE(load_timing_info.send_start, load_timing_info.send_end); |
| EXPECT_LE(load_timing_info.send_end, load_timing_info.receive_headers_start); |
| EXPECT_LE(load_timing_info.receive_headers_start, |
| load_timing_info.receive_headers_end); |
| } |
| |
| // Same as above, but with a reused socket and proxy times. |
| void TestLoadTimingReusedWithProxy(const LoadTimingInfo& load_timing_info) { |
| EXPECT_TRUE(load_timing_info.socket_reused); |
| EXPECT_NE(NetLogSource::kInvalidId, load_timing_info.socket_log_id); |
| |
| EXPECT_FALSE(load_timing_info.request_start_time.is_null()); |
| EXPECT_FALSE(load_timing_info.request_start.is_null()); |
| |
| ExpectConnectTimingHasNoTimes(load_timing_info.connect_timing); |
| |
| EXPECT_LE(load_timing_info.request_start, |
| load_timing_info.proxy_resolve_start); |
| EXPECT_LE(load_timing_info.proxy_resolve_start, |
| load_timing_info.proxy_resolve_end); |
| EXPECT_LE(load_timing_info.proxy_resolve_end, load_timing_info.send_start); |
| EXPECT_LE(load_timing_info.send_start, load_timing_info.send_end); |
| EXPECT_LE(load_timing_info.send_end, load_timing_info.receive_headers_start); |
| EXPECT_LE(load_timing_info.receive_headers_start, |
| load_timing_info.receive_headers_end); |
| } |
| |
| CookieList GetAllCookies(URLRequestContext* request_context) { |
| CookieList cookie_list; |
| base::RunLoop run_loop; |
| request_context->cookie_store()->GetAllCookiesAsync( |
| base::BindLambdaForTesting([&](const CookieList& cookies) { |
| cookie_list = cookies; |
| run_loop.Quit(); |
| })); |
| run_loop.Run(); |
| return cookie_list; |
| } |
| |
| void TestLoadTimingCacheHitNoNetwork(const LoadTimingInfo& load_timing_info) { |
| EXPECT_FALSE(load_timing_info.socket_reused); |
| EXPECT_EQ(NetLogSource::kInvalidId, load_timing_info.socket_log_id); |
| |
| EXPECT_FALSE(load_timing_info.request_start_time.is_null()); |
| EXPECT_FALSE(load_timing_info.request_start.is_null()); |
| |
| ExpectConnectTimingHasNoTimes(load_timing_info.connect_timing); |
| EXPECT_LE(load_timing_info.request_start, load_timing_info.send_start); |
| EXPECT_LE(load_timing_info.send_start, load_timing_info.send_end); |
| EXPECT_LE(load_timing_info.send_end, load_timing_info.receive_headers_start); |
| EXPECT_LE(load_timing_info.receive_headers_start, |
| load_timing_info.receive_headers_end); |
| |
| EXPECT_TRUE(load_timing_info.proxy_resolve_start.is_null()); |
| EXPECT_TRUE(load_timing_info.proxy_resolve_end.is_null()); |
| } |
| |
| // Job that allows monitoring of its priority. |
| class PriorityMonitoringURLRequestJob : public URLRequestTestJob { |
| public: |
| // The latest priority of the job is always written to |request_priority_|. |
| PriorityMonitoringURLRequestJob(URLRequest* request, |
| RequestPriority* request_priority) |
| : URLRequestTestJob(request), request_priority_(request_priority) { |
| *request_priority_ = DEFAULT_PRIORITY; |
| } |
| |
| void SetPriority(RequestPriority priority) override { |
| *request_priority_ = priority; |
| URLRequestTestJob::SetPriority(priority); |
| } |
| |
| private: |
| const raw_ptr<RequestPriority> request_priority_; |
| }; |
| |
| // Do a case-insensitive search through |haystack| for |needle|. |
| bool ContainsString(const std::string& haystack, const char* needle) { |
| std::string::const_iterator it = |
| base::ranges::search(haystack, base::StringPiece(needle), |
| base::CaseInsensitiveCompareASCII<char>()); |
| return it != haystack.end(); |
| } |
| |
| std::unique_ptr<UploadDataStream> CreateSimpleUploadData(const char* data) { |
| auto reader = std::make_unique<UploadBytesElementReader>(data, strlen(data)); |
| return ElementsUploadDataStream::CreateWithReader(std::move(reader), 0); |
| } |
| |
| // Verify that the SSLInfo of a successful SSL connection has valid values. |
| void CheckSSLInfo(const SSLInfo& ssl_info) { |
| // The cipher suite TLS_NULL_WITH_NULL_NULL (0) must not be negotiated. |
| uint16_t cipher_suite = |
| SSLConnectionStatusToCipherSuite(ssl_info.connection_status); |
| EXPECT_NE(0U, cipher_suite); |
| } |
| |
| // A network delegate that allows the user to choose a subset of request stages |
| // to block in. When blocking, the delegate can do one of the following: |
| // * synchronously return a pre-specified error code, or |
| // * asynchronously return that value via an automatically called callback, |
| // or |
| // * block and wait for the user to do a callback. |
| // Additionally, the user may also specify a redirect URL -- then each request |
| // with the current URL different from the redirect target will be redirected |
| // to that target, in the on-before-URL-request stage, independent of whether |
| // the delegate blocks in ON_BEFORE_URL_REQUEST or not. |
| class BlockingNetworkDelegate : public TestNetworkDelegate { |
| public: |
| // Stages in which the delegate can block. |
| enum Stage { |
| NOT_BLOCKED = 0, |
| ON_BEFORE_URL_REQUEST = 1 << 0, |
| ON_BEFORE_SEND_HEADERS = 1 << 1, |
| ON_HEADERS_RECEIVED = 1 << 2, |
| }; |
| |
| // Behavior during blocked stages. During other stages, just |
| // returns OK or NetworkDelegate::AUTH_REQUIRED_RESPONSE_NO_ACTION. |
| enum BlockMode { |
| SYNCHRONOUS, // No callback, returns specified return values. |
| AUTO_CALLBACK, // |this| posts a task to run the callback using the |
| // specified return codes. |
| USER_CALLBACK, // User takes care of doing a callback. |retval_| and |
| // |auth_retval_| are ignored. In every blocking stage the |
| // message loop is quit. |
| }; |
| |
| // Creates a delegate which does not block at all. |
| explicit BlockingNetworkDelegate(BlockMode block_mode); |
| |
| BlockingNetworkDelegate(const BlockingNetworkDelegate&) = delete; |
| BlockingNetworkDelegate& operator=(const BlockingNetworkDelegate&) = delete; |
| |
| // Runs the message loop until the delegate blocks. |
| void RunUntilBlocked(); |
| |
| // For users to trigger a callback returning |response|. |
| // Side-effects: resets |stage_blocked_for_callback_| and stored callbacks. |
| // Only call if |block_mode_| == USER_CALLBACK. |
| void DoCallback(int response); |
| |
| // Setters. |
| void set_retval(int retval) { |
| ASSERT_NE(USER_CALLBACK, block_mode_); |
| ASSERT_NE(ERR_IO_PENDING, retval); |
| ASSERT_NE(OK, retval); |
| retval_ = retval; |
| } |
| void set_redirect_url(const GURL& url) { redirect_url_ = url; } |
| |
| void set_block_on(int block_on) { block_on_ = block_on; } |
| |
| // Allows the user to check in which state did we block. |
| Stage stage_blocked_for_callback() const { |
| EXPECT_EQ(USER_CALLBACK, block_mode_); |
| return stage_blocked_for_callback_; |
| } |
| |
| private: |
| void OnBlocked(); |
| |
| void RunCallback(int response, CompletionOnceCallback callback); |
| |
| // TestNetworkDelegate implementation. |
| int OnBeforeURLRequest(URLRequest* request, |
| CompletionOnceCallback callback, |
| GURL* new_url) override; |
| |
| int OnBeforeStartTransaction( |
| URLRequest* request, |
| const HttpRequestHeaders& headers, |
| OnBeforeStartTransactionCallback callback) override; |
| |
| int OnHeadersReceived( |
| URLRequest* request, |
| CompletionOnceCallback callback, |
| const HttpResponseHeaders* original_response_headers, |
| scoped_refptr<HttpResponseHeaders>* override_response_headers, |
| const IPEndPoint& endpoint, |
| absl::optional<GURL>* preserve_fragment_on_redirect_url) override; |
| |
| // Resets the callbacks and |stage_blocked_for_callback_|. |
| void Reset(); |
| |
| // Checks whether we should block in |stage|. If yes, returns an error code |
| // and optionally sets up callback based on |block_mode_|. If no, returns OK. |
| int MaybeBlockStage(Stage stage, CompletionOnceCallback callback); |
| |
| // Configuration parameters, can be adjusted by public methods: |
| const BlockMode block_mode_; |
| |
| // Values returned on blocking stages when mode is SYNCHRONOUS or |
| // AUTO_CALLBACK. For USER_CALLBACK these are set automatically to IO_PENDING. |
| int retval_ = OK; |
| |
| GURL redirect_url_; // Used if non-empty during OnBeforeURLRequest. |
| int block_on_ = 0; // Bit mask: in which stages to block. |
| |
| // Internal variables, not set by not the user: |
| // Last blocked stage waiting for user callback (unused if |block_mode_| != |
| // USER_CALLBACK). |
| Stage stage_blocked_for_callback_ = NOT_BLOCKED; |
| |
| // Callback objects stored during blocking stages. |
| CompletionOnceCallback callback_; |
| |
| // Closure to run to exit RunUntilBlocked(). |
| base::OnceClosure on_blocked_; |
| |
| base::WeakPtrFactory<BlockingNetworkDelegate> weak_factory_{this}; |
| }; |
| |
| BlockingNetworkDelegate::BlockingNetworkDelegate(BlockMode block_mode) |
| : block_mode_(block_mode) {} |
| |
| void BlockingNetworkDelegate::RunUntilBlocked() { |
| base::RunLoop run_loop; |
| on_blocked_ = run_loop.QuitClosure(); |
| run_loop.Run(); |
| } |
| |
| void BlockingNetworkDelegate::DoCallback(int response) { |
| ASSERT_EQ(USER_CALLBACK, block_mode_); |
| ASSERT_NE(NOT_BLOCKED, stage_blocked_for_callback_); |
| CompletionOnceCallback callback = std::move(callback_); |
| Reset(); |
| |
| // |callback| may trigger completion of a request, so post it as a task, so |
| // it will run under a subsequent TestDelegate::RunUntilComplete() loop. |
| base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask( |
| FROM_HERE, base::BindOnce(&BlockingNetworkDelegate::RunCallback, |
| weak_factory_.GetWeakPtr(), response, |
| std::move(callback))); |
| } |
| |
| void BlockingNetworkDelegate::OnBlocked() { |
| // If this fails due to |on_blocked_| being null then OnBlocked() was run by |
| // a RunLoop other than RunUntilBlocked(), indicating a bug in the calling |
| // test. |
| std::move(on_blocked_).Run(); |
| } |
| |
| void BlockingNetworkDelegate::RunCallback(int response, |
| CompletionOnceCallback callback) { |
| std::move(callback).Run(response); |
| } |
| |
| int BlockingNetworkDelegate::OnBeforeURLRequest(URLRequest* request, |
| CompletionOnceCallback callback, |
| GURL* new_url) { |
| if (redirect_url_ == request->url()) |
| return OK; // We've already seen this request and redirected elsewhere. |
| |
| // TestNetworkDelegate always completes synchronously. |
| CHECK_NE(ERR_IO_PENDING, TestNetworkDelegate::OnBeforeURLRequest( |
| request, base::NullCallback(), new_url)); |
| |
| if (!redirect_url_.is_empty()) |
| *new_url = redirect_url_; |
| |
| return MaybeBlockStage(ON_BEFORE_URL_REQUEST, std::move(callback)); |
| } |
| |
| int BlockingNetworkDelegate::OnBeforeStartTransaction( |
| URLRequest* request, |
| const HttpRequestHeaders& headers, |
| OnBeforeStartTransactionCallback callback) { |
| // TestNetworkDelegate always completes synchronously. |
| CHECK_NE(ERR_IO_PENDING, TestNetworkDelegate::OnBeforeStartTransaction( |
| request, headers, base::NullCallback())); |
| |
| return MaybeBlockStage( |
| ON_BEFORE_SEND_HEADERS, |
| base::BindOnce( |
| [](OnBeforeStartTransactionCallback callback, int result) { |
| std::move(callback).Run(result, absl::nullopt); |
| }, |
| std::move(callback))); |
| } |
| |
| int BlockingNetworkDelegate::OnHeadersReceived( |
| URLRequest* request, |
| CompletionOnceCallback callback, |
| const HttpResponseHeaders* original_response_headers, |
| scoped_refptr<HttpResponseHeaders>* override_response_headers, |
| const IPEndPoint& endpoint, |
| absl::optional<GURL>* preserve_fragment_on_redirect_url) { |
| // TestNetworkDelegate always completes synchronously. |
| CHECK_NE(ERR_IO_PENDING, |
| TestNetworkDelegate::OnHeadersReceived( |
| request, base::NullCallback(), original_response_headers, |
| override_response_headers, endpoint, |
| preserve_fragment_on_redirect_url)); |
| |
| return MaybeBlockStage(ON_HEADERS_RECEIVED, std::move(callback)); |
| } |
| |
| void BlockingNetworkDelegate::Reset() { |
| EXPECT_NE(NOT_BLOCKED, stage_blocked_for_callback_); |
| stage_blocked_for_callback_ = NOT_BLOCKED; |
| callback_.Reset(); |
| } |
| |
| int BlockingNetworkDelegate::MaybeBlockStage( |
| BlockingNetworkDelegate::Stage stage, |
| CompletionOnceCallback callback) { |
| // Check that the user has provided callback for the previous blocked stage. |
| EXPECT_EQ(NOT_BLOCKED, stage_blocked_for_callback_); |
| |
| if ((block_on_ & stage) == 0) { |
| return OK; |
| } |
| |
| switch (block_mode_) { |
| case SYNCHRONOUS: |
| EXPECT_NE(OK, retval_); |
| return retval_; |
| |
| case AUTO_CALLBACK: |
| base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask( |
| FROM_HERE, base::BindOnce(&BlockingNetworkDelegate::RunCallback, |
| weak_factory_.GetWeakPtr(), retval_, |
| std::move(callback))); |
| return ERR_IO_PENDING; |
| |
| case USER_CALLBACK: |
| callback_ = std::move(callback); |
| stage_blocked_for_callback_ = stage; |
| // We may reach here via a callback prior to RunUntilBlocked(), so post |
| // a task to fetch and run the |on_blocked_| closure. |
| base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask( |
| FROM_HERE, base::BindOnce(&BlockingNetworkDelegate::OnBlocked, |
| weak_factory_.GetWeakPtr())); |
| return ERR_IO_PENDING; |
| } |
| NOTREACHED(); |
| return 0; |
| } |
| |
| // A mock ReportSenderInterface that just remembers the latest report |
| // URI and report to be sent. |
| class MockCertificateReportSender |
| : public TransportSecurityState::ReportSenderInterface { |
| public: |
| MockCertificateReportSender() = default; |
| ~MockCertificateReportSender() override = default; |
| |
| void Send( |
| const GURL& report_uri, |
| base::StringPiece content_type, |
| base::StringPiece report, |
| const NetworkAnonymizationKey& network_anonymization_key, |
| 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()); |
| latest_network_anonymization_key_ = network_anonymization_key; |
| } |
| const GURL& latest_report_uri() { return latest_report_uri_; } |
| const std::string& latest_report() { return latest_report_; } |
| const std::string& latest_content_type() { return latest_content_type_; } |
| const NetworkAnonymizationKey& latest_network_anonymization_key() { |
| return latest_network_anonymization_key_; |
| } |
| |
| private: |
| GURL latest_report_uri_; |
| std::string latest_report_; |
| std::string latest_content_type_; |
| NetworkAnonymizationKey latest_network_anonymization_key_; |
| }; |
| |
| // OCSPErrorTestDelegate caches the SSLInfo passed to OnSSLCertificateError. |
| // This is needed because after the certificate failure, the URLRequest will |
| // retry the connection, and return a partial SSLInfo with a cached cert status. |
| // The partial SSLInfo does not have the OCSP information filled out. |
| class OCSPErrorTestDelegate : public TestDelegate { |
| public: |
| void OnSSLCertificateError(URLRequest* request, |
| int net_error, |
| const SSLInfo& ssl_info, |
| bool fatal) override { |
| ssl_info_ = ssl_info; |
| on_ssl_certificate_error_called_ = true; |
| TestDelegate::OnSSLCertificateError(request, net_error, ssl_info, fatal); |
| } |
| |
| bool on_ssl_certificate_error_called() { |
| return on_ssl_certificate_error_called_; |
| } |
| |
| SSLInfo ssl_info() { return ssl_info_; } |
| |
| private: |
| bool on_ssl_certificate_error_called_ = false; |
| SSLInfo ssl_info_; |
| }; |
| |
| #if !BUILDFLAG(IS_IOS) |
| // Compute the root cert's SPKI hash on the fly, to avoid hardcoding it within |
| // tests. |
| bool GetTestRootCertSPKIHash(SHA256HashValue* root_hash) { |
| scoped_refptr<X509Certificate> root_cert = |
| ImportCertFromFile(GetTestCertsDirectory(), "root_ca_cert.pem"); |
| if (!root_cert) |
| return false; |
| base::StringPiece root_spki; |
| if (!asn1::ExtractSPKIFromDERCert( |
| x509_util::CryptoBufferAsStringPiece(root_cert->cert_buffer()), |
| &root_spki)) { |
| return false; |
| } |
| crypto::SHA256HashString(root_spki, root_hash, sizeof(SHA256HashValue)); |
| return true; |
| } |
| #endif |
| |
| } // namespace |
| |
| // Inherit PlatformTest since we require the autorelease pool on Mac OS X. |
| class URLRequestTest : public PlatformTest, public WithTaskEnvironment { |
| public: |
| URLRequestTest() = default; |
| |
| ~URLRequestTest() override { |
| // URLRequestJobs may post clean-up tasks on destruction. |
| base::RunLoop().RunUntilIdle(); |
| |
| SetTransportSecurityStateSourceForTesting(nullptr); |
| } |
| |
| void SetUp() override { |
| auto context_builder = CreateTestURLRequestContextBuilder(); |
| context_builder->set_net_log(NetLog::Get()); |
| SetUpContextBuilder(*context_builder); |
| // We set the TestNetworkDelegate after calling SetUpContextBuilder as |
| // default_network_delegate() relies on this set up and we don't want to |
| // allow subclasses to break the assumption. |
| context_builder->set_network_delegate( |
| std::make_unique<TestNetworkDelegate>()); |
| default_context_ = context_builder->Build(); |
| PlatformTest::SetUp(); |
| } |
| |
| void TearDown() override { default_context_.reset(); } |
| |
| virtual void SetUpContextBuilder(URLRequestContextBuilder& builder) {} |
| |
| TestNetworkDelegate& default_network_delegate() { |
| // This cast is safe because we provided a TestNetworkDelegate in SetUp(). |
| return *static_cast<TestNetworkDelegate*>( |
| default_context_->network_delegate()); |
| } |
| |
| URLRequestContext& default_context() const { return *default_context_; } |
| |
| // Creates a temp test file and writes |data| to the file. The file will be |
| // deleted after the test completes. |
| void CreateTestFile(const char* data, |
| size_t data_size, |
| base::FilePath* test_file) { |
| ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); |
| // Get an absolute path since |temp_dir| can contain a symbolic link. As of |
| // now, Mac and Android bots return a path with a symbolic link. |
| base::FilePath absolute_temp_dir = |
| base::MakeAbsoluteFilePath(temp_dir_.GetPath()); |
| |
| ASSERT_TRUE(base::CreateTemporaryFileInDir(absolute_temp_dir, test_file)); |
| ASSERT_TRUE( |
| base::WriteFile(*test_file, base::StringPiece(data, data_size))); |
| } |
| |
| static std::unique_ptr<ConfiguredProxyResolutionService> |
| CreateFixedProxyResolutionService(const std::string& proxy) { |
| return ConfiguredProxyResolutionService::CreateFixedForTest( |
| proxy, TRAFFIC_ANNOTATION_FOR_TESTS); |
| } |
| |
| std::unique_ptr<URLRequest> CreateFirstPartyRequest( |
| const URLRequestContext& context, |
| const GURL& url, |
| URLRequest::Delegate* delegate) { |
| auto req = context.CreateRequest(url, DEFAULT_PRIORITY, delegate, |
| TRAFFIC_ANNOTATION_FOR_TESTS); |
| req->set_initiator(url::Origin::Create(url)); |
| req->set_site_for_cookies(SiteForCookies::FromUrl(url)); |
| return req; |
| } |
| |
| protected: |
| RecordingNetLogObserver net_log_observer_; |
| std::unique_ptr<URLRequestContext> default_context_; |
| base::ScopedTempDir temp_dir_; |
| }; |
| |
| TEST_F(URLRequestTest, AboutBlankTest) { |
| TestDelegate d; |
| { |
| std::unique_ptr<URLRequest> r( |
| default_context().CreateRequest(GURL("about:blank"), DEFAULT_PRIORITY, |
| &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| |
| r->Start(); |
| EXPECT_TRUE(r->is_pending()); |
| |
| d.RunUntilComplete(); |
| |
| EXPECT_TRUE(!r->is_pending()); |
| EXPECT_FALSE(d.received_data_before_response()); |
| EXPECT_EQ(d.bytes_received(), 0); |
| EXPECT_TRUE(r->GetResponseRemoteEndpoint().address().empty()); |
| EXPECT_EQ(0, r->GetResponseRemoteEndpoint().port()); |
| } |
| } |
| |
| TEST_F(URLRequestTest, InvalidUrlTest) { |
| TestDelegate d; |
| { |
| std::unique_ptr<URLRequest> r( |
| default_context().CreateRequest(GURL("invalid url"), DEFAULT_PRIORITY, |
| &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| |
| r->Start(); |
| EXPECT_TRUE(r->is_pending()); |
| |
| d.RunUntilComplete(); |
| EXPECT_TRUE(d.request_failed()); |
| } |
| } |
| |
| // Test that URLRequest rejects WS URLs by default. |
| TEST_F(URLRequestTest, WsUrlTest) { |
| const url::Origin kOrigin = url::Origin::Create(GURL("http://foo.test/")); |
| |
| TestDelegate d; |
| std::unique_ptr<URLRequest> r( |
| default_context().CreateRequest(GURL("ws://foo.test/"), DEFAULT_PRIORITY, |
| &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| // This is not strictly necessary for this test, but used to trigger a DCHECK. |
| // See https://crbug.com/1245115. |
| r->set_isolation_info( |
| IsolationInfo::Create(IsolationInfo::RequestType::kMainFrame, kOrigin, |
| kOrigin, SiteForCookies::FromOrigin(kOrigin))); |
| |
| r->Start(); |
| d.RunUntilComplete(); |
| EXPECT_TRUE(d.request_failed()); |
| EXPECT_THAT(d.request_status(), IsError(ERR_UNKNOWN_URL_SCHEME)); |
| } |
| |
| // Test that URLRequest rejects WSS URLs by default. |
| TEST_F(URLRequestTest, WssUrlTest) { |
| const url::Origin kOrigin = url::Origin::Create(GURL("https://foo.test/")); |
| |
| TestDelegate d; |
| std::unique_ptr<URLRequest> r( |
| default_context().CreateRequest(GURL("wss://foo.test/"), DEFAULT_PRIORITY, |
| &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| // This is not strictly necessary for this test, but used to trigger a DCHECK. |
| // See https://crbug.com/1245115. |
| r->set_isolation_info( |
| IsolationInfo::Create(IsolationInfo::RequestType::kMainFrame, kOrigin, |
| kOrigin, SiteForCookies::FromOrigin(kOrigin))); |
| |
| r->Start(); |
| d.RunUntilComplete(); |
| EXPECT_TRUE(d.request_failed()); |
| EXPECT_THAT(d.request_status(), IsError(ERR_UNKNOWN_URL_SCHEME)); |
| } |
| |
| TEST_F(URLRequestTest, InvalidReferrerTest) { |
| default_network_delegate().set_cancel_request_with_policy_violating_referrer( |
| true); |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req = default_context().CreateRequest( |
| GURL("http://localhost/"), DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS); |
| req->SetReferrer("https://somewhere.com/"); |
| |
| req->Start(); |
| d.RunUntilComplete(); |
| EXPECT_TRUE(d.request_failed()); |
| } |
| |
| TEST_F(URLRequestTest, RecordsSameOriginReferrerHistogram) { |
| default_network_delegate().set_cancel_request_with_policy_violating_referrer( |
| false); |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req(default_context().CreateRequest( |
| GURL("http://google.com/"), DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->SetReferrer("http://google.com"); |
| req->set_referrer_policy(ReferrerPolicy::NEVER_CLEAR); |
| |
| base::HistogramTester histograms; |
| |
| req->Start(); |
| d.RunUntilComplete(); |
| histograms.ExpectUniqueSample( |
| "Net.URLRequest.ReferrerPolicyForRequest.SameOrigin", |
| static_cast<int>(ReferrerPolicy::NEVER_CLEAR), 1); |
| } |
| |
| TEST_F(URLRequestTest, RecordsCrossOriginReferrerHistogram) { |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req(default_context().CreateRequest( |
| GURL("http://google.com/"), DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->SetReferrer("http://origin.com"); |
| |
| // Set a different policy just to make sure we aren't always logging the same |
| // policy. |
| req->set_referrer_policy( |
| ReferrerPolicy::CLEAR_ON_TRANSITION_FROM_SECURE_TO_INSECURE); |
| |
| base::HistogramTester histograms; |
| |
| req->Start(); |
| d.RunUntilComplete(); |
| histograms.ExpectUniqueSample( |
| "Net.URLRequest.ReferrerPolicyForRequest.CrossOrigin", |
| static_cast<int>( |
| ReferrerPolicy::CLEAR_ON_TRANSITION_FROM_SECURE_TO_INSECURE), |
| 1); |
| } |
| |
| TEST_F(URLRequestTest, RecordsReferrerHistogramAgainOnRedirect) { |
| auto context_builder = CreateTestURLRequestContextBuilder(); |
| auto network_delegate = std::make_unique<BlockingNetworkDelegate>( |
| BlockingNetworkDelegate::SYNCHRONOUS); |
| network_delegate->set_redirect_url(GURL("http://redirect.com/")); |
| context_builder->set_network_delegate(std::move(network_delegate)); |
| auto context = context_builder->Build(); |
| |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req( |
| context->CreateRequest(GURL("http://google.com/"), DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->SetReferrer("http://google.com"); |
| |
| req->set_referrer_policy( |
| ReferrerPolicy::CLEAR_ON_TRANSITION_FROM_SECURE_TO_INSECURE); |
| |
| base::HistogramTester histograms; |
| |
| req->Start(); |
| d.RunUntilRedirect(); |
| histograms.ExpectUniqueSample( |
| "Net.URLRequest.ReferrerPolicyForRequest.SameOrigin", |
| static_cast<int>( |
| ReferrerPolicy::CLEAR_ON_TRANSITION_FROM_SECURE_TO_INSECURE), |
| 1); |
| req->FollowDeferredRedirect(/*removed_headers=*/absl::nullopt, |
| /*modified_headers=*/absl::nullopt); |
| d.RunUntilComplete(); |
| histograms.ExpectUniqueSample( |
| "Net.URLRequest.ReferrerPolicyForRequest.CrossOrigin", |
| static_cast<int>( |
| ReferrerPolicy::CLEAR_ON_TRANSITION_FROM_SECURE_TO_INSECURE), |
| 1); |
| } |
| |
| TEST_F(URLRequestTest, RecordsReferrrerWithInformativePath) { |
| auto context_builder = CreateTestURLRequestContextBuilder(); |
| auto network_delegate = std::make_unique<BlockingNetworkDelegate>( |
| BlockingNetworkDelegate::SYNCHRONOUS); |
| network_delegate->set_cancel_request_with_policy_violating_referrer(true); |
| network_delegate->set_redirect_url(GURL("http://redirect.com/")); |
| context_builder->set_network_delegate(std::move(network_delegate)); |
| auto context = context_builder->Build(); |
| |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req( |
| context->CreateRequest(GURL("http://google.com/"), DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| |
| // Since this referrer is much more informative than the initiating origin, |
| // we should see the histograms' true buckets populated. |
| req->SetReferrer("http://google.com/very-informative-path"); |
| |
| base::HistogramTester histograms; |
| |
| req->Start(); |
| d.RunUntilRedirect(); |
| histograms.ExpectUniqueSample( |
| "Net.URLRequest.ReferrerHasInformativePath.SameOrigin", |
| /* Check the count of the "true" bucket in the boolean histogram. */ true, |
| 1); |
| req->FollowDeferredRedirect(/*removed_headers=*/absl::nullopt, |
| /*modified_headers=*/absl::nullopt); |
| d.RunUntilComplete(); |
| histograms.ExpectUniqueSample( |
| "Net.URLRequest.ReferrerHasInformativePath.CrossOrigin", true, 1); |
| } |
| |
| TEST_F(URLRequestTest, RecordsReferrerWithInformativeQuery) { |
| auto context_builder = CreateTestURLRequestContextBuilder(); |
| auto network_delegate = std::make_unique<BlockingNetworkDelegate>( |
| BlockingNetworkDelegate::SYNCHRONOUS); |
| network_delegate->set_cancel_request_with_policy_violating_referrer(true); |
| network_delegate->set_redirect_url(GURL("http://redirect.com/")); |
| context_builder->set_network_delegate(std::move(network_delegate)); |
| auto context = context_builder->Build(); |
| |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req( |
| context->CreateRequest(GURL("http://google.com/"), DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| |
| // Since this referrer is much more informative than the initiating origin, |
| // we should see the histograms' true buckets populated. |
| req->SetReferrer("http://google.com/?very-informative-query"); |
| |
| base::HistogramTester histograms; |
| |
| req->Start(); |
| d.RunUntilRedirect(); |
| histograms.ExpectUniqueSample( |
| "Net.URLRequest.ReferrerHasInformativePath.SameOrigin", |
| /* Check the count of the "true" bucket in the boolean histogram. */ true, |
| 1); |
| req->FollowDeferredRedirect(/*removed_headers=*/absl::nullopt, |
| /*modified_headers=*/absl::nullopt); |
| d.RunUntilComplete(); |
| histograms.ExpectUniqueSample( |
| "Net.URLRequest.ReferrerHasInformativePath.CrossOrigin", true, 1); |
| } |
| |
| TEST_F(URLRequestTest, RecordsReferrerWithoutInformativePathOrQuery) { |
| auto context_builder = CreateTestURLRequestContextBuilder(); |
| auto network_delegate = std::make_unique<BlockingNetworkDelegate>( |
| BlockingNetworkDelegate::SYNCHRONOUS); |
| network_delegate->set_cancel_request_with_policy_violating_referrer(false); |
| network_delegate->set_redirect_url(GURL("http://origin.com/")); |
| context_builder->set_network_delegate(std::move(network_delegate)); |
| auto context = context_builder->Build(); |
| |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req( |
| context->CreateRequest(GURL("http://google.com/"), DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| |
| // Since this referrer _isn't_ more informative than the initiating origin, |
| // we should see the histograms' false buckets populated. |
| req->SetReferrer("http://origin.com"); |
| |
| base::HistogramTester histograms; |
| |
| req->Start(); |
| d.RunUntilRedirect(); |
| histograms.ExpectUniqueSample( |
| "Net.URLRequest.ReferrerHasInformativePath.CrossOrigin", false, 1); |
| req->FollowDeferredRedirect(/*removed_headers=*/absl::nullopt, |
| /*modified_headers=*/absl::nullopt); |
| d.RunUntilComplete(); |
| histograms.ExpectUniqueSample( |
| "Net.URLRequest.ReferrerHasInformativePath.SameOrigin", false, 1); |
| } |
| |
| // A URLRequestInterceptor that allows setting the LoadTimingInfo value of the |
| // URLRequestJobs it creates. |
| class URLRequestInterceptorWithLoadTimingInfo : public URLRequestInterceptor { |
| public: |
| // Static getters for canned response header and data strings. |
| static std::string ok_data() { return URLRequestTestJob::test_data_1(); } |
| |
| static std::string ok_headers() { return URLRequestTestJob::test_headers(); } |
| |
| URLRequestInterceptorWithLoadTimingInfo() = default; |
| ~URLRequestInterceptorWithLoadTimingInfo() override = default; |
| |
| // URLRequestInterceptor implementation: |
| std::unique_ptr<URLRequestJob> MaybeInterceptRequest( |
| URLRequest* request) const override { |
| std::unique_ptr<URLRequestTestJob> job = |
| std::make_unique<URLRequestTestJob>(request, ok_headers(), ok_data(), |
| true); |
| job->set_load_timing_info(main_request_load_timing_info_); |
| return job; |
| } |
| |
| void set_main_request_load_timing_info( |
| const LoadTimingInfo& main_request_load_timing_info) { |
| main_request_load_timing_info_ = main_request_load_timing_info; |
| } |
| |
| private: |
| mutable LoadTimingInfo main_request_load_timing_info_; |
| }; |
| |
| // These tests inject a MockURLRequestInterceptor |
| class URLRequestLoadTimingTest : public URLRequestTest { |
| public: |
| URLRequestLoadTimingTest() { |
| std::unique_ptr<URLRequestInterceptorWithLoadTimingInfo> interceptor = |
| std::make_unique<URLRequestInterceptorWithLoadTimingInfo>(); |
| interceptor_ = interceptor.get(); |
| URLRequestFilter::GetInstance()->AddHostnameInterceptor( |
| "http", "test_intercept", std::move(interceptor)); |
| } |
| |
| ~URLRequestLoadTimingTest() override { |
| URLRequestFilter::GetInstance()->ClearHandlers(); |
| } |
| |
| URLRequestInterceptorWithLoadTimingInfo* interceptor() const { |
| return interceptor_; |
| } |
| |
| private: |
| raw_ptr<URLRequestInterceptorWithLoadTimingInfo> interceptor_; |
| }; |
| |
| // "Normal" LoadTimingInfo as returned by a job. Everything is in order, not |
| // reused. |connect_time_flags| is used to indicate if there should be dns |
| // or SSL times, and |used_proxy| is used for proxy times. |
| LoadTimingInfo NormalLoadTimingInfo(base::TimeTicks now, |
| int connect_time_flags, |
| bool used_proxy) { |
| LoadTimingInfo load_timing; |
| load_timing.socket_log_id = 1; |
| |
| if (used_proxy) { |
| load_timing.proxy_resolve_start = now + base::Days(1); |
| load_timing.proxy_resolve_end = now + base::Days(2); |
| } |
| |
| LoadTimingInfo::ConnectTiming& connect_timing = load_timing.connect_timing; |
| if (connect_time_flags & CONNECT_TIMING_HAS_DNS_TIMES) { |
| connect_timing.domain_lookup_start = now + base::Days(3); |
| connect_timing.domain_lookup_end = now + base::Days(4); |
| } |
| connect_timing.connect_start = now + base::Days(5); |
| if (connect_time_flags & CONNECT_TIMING_HAS_SSL_TIMES) { |
| connect_timing.ssl_start = now + base::Days(6); |
| connect_timing.ssl_end = now + base::Days(7); |
| } |
| connect_timing.connect_end = now + base::Days(8); |
| |
| load_timing.send_start = now + base::Days(9); |
| load_timing.send_end = now + base::Days(10); |
| load_timing.receive_headers_start = now + base::Days(11); |
| load_timing.receive_headers_end = now + base::Days(12); |
| return load_timing; |
| } |
| |
| // Same as above, but in the case of a reused socket. |
| LoadTimingInfo NormalLoadTimingInfoReused(base::TimeTicks now, |
| bool used_proxy) { |
| LoadTimingInfo load_timing; |
| load_timing.socket_log_id = 1; |
| load_timing.socket_reused = true; |
| |
| if (used_proxy) { |
| load_timing.proxy_resolve_start = now + base::Days(1); |
| load_timing.proxy_resolve_end = now + base::Days(2); |
| } |
| |
| load_timing.send_start = now + base::Days(9); |
| load_timing.send_end = now + base::Days(10); |
| load_timing.receive_headers_start = now + base::Days(11); |
| load_timing.receive_headers_end = now + base::Days(12); |
| return load_timing; |
| } |
| |
| LoadTimingInfo RunURLRequestInterceptorLoadTimingTest( |
| const LoadTimingInfo& job_load_timing, |
| const URLRequestContext& context, |
| URLRequestInterceptorWithLoadTimingInfo* interceptor) { |
| interceptor->set_main_request_load_timing_info(job_load_timing); |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req( |
| context.CreateRequest(GURL("http://test_intercept/foo"), DEFAULT_PRIORITY, |
| &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->Start(); |
| d.RunUntilComplete(); |
| |
| LoadTimingInfo resulting_load_timing; |
| req->GetLoadTimingInfo(&resulting_load_timing); |
| |
| // None of these should be modified by the URLRequest. |
| EXPECT_EQ(job_load_timing.socket_reused, resulting_load_timing.socket_reused); |
| EXPECT_EQ(job_load_timing.socket_log_id, resulting_load_timing.socket_log_id); |
| EXPECT_EQ(job_load_timing.send_start, resulting_load_timing.send_start); |
| EXPECT_EQ(job_load_timing.send_end, resulting_load_timing.send_end); |
| EXPECT_EQ(job_load_timing.receive_headers_start, |
| resulting_load_timing.receive_headers_start); |
| EXPECT_EQ(job_load_timing.receive_headers_end, |
| resulting_load_timing.receive_headers_end); |
| EXPECT_EQ(job_load_timing.push_start, resulting_load_timing.push_start); |
| EXPECT_EQ(job_load_timing.push_end, resulting_load_timing.push_end); |
| |
| return resulting_load_timing; |
| } |
| |
| // Basic test that the intercept + load timing tests work. |
| TEST_F(URLRequestLoadTimingTest, InterceptLoadTiming) { |
| base::TimeTicks now = base::TimeTicks::Now(); |
| LoadTimingInfo job_load_timing = |
| NormalLoadTimingInfo(now, CONNECT_TIMING_HAS_DNS_TIMES, false); |
| |
| LoadTimingInfo load_timing_result = RunURLRequestInterceptorLoadTimingTest( |
| job_load_timing, default_context(), interceptor()); |
| |
| // Nothing should have been changed by the URLRequest. |
| EXPECT_EQ(job_load_timing.proxy_resolve_start, |
| load_timing_result.proxy_resolve_start); |
| EXPECT_EQ(job_load_timing.proxy_resolve_end, |
| load_timing_result.proxy_resolve_end); |
| EXPECT_EQ(job_load_timing.connect_timing.domain_lookup_start, |
| load_timing_result.connect_timing.domain_lookup_start); |
| EXPECT_EQ(job_load_timing.connect_timing.domain_lookup_end, |
| load_timing_result.connect_timing.domain_lookup_end); |
| EXPECT_EQ(job_load_timing.connect_timing.connect_start, |
| load_timing_result.connect_timing.connect_start); |
| EXPECT_EQ(job_load_timing.connect_timing.connect_end, |
| load_timing_result.connect_timing.connect_end); |
| EXPECT_EQ(job_load_timing.connect_timing.ssl_start, |
| load_timing_result.connect_timing.ssl_start); |
| EXPECT_EQ(job_load_timing.connect_timing.ssl_end, |
| load_timing_result.connect_timing.ssl_end); |
| |
| // Redundant sanity check. |
| TestLoadTimingNotReused(load_timing_result, CONNECT_TIMING_HAS_DNS_TIMES); |
| } |
| |
| // Another basic test, with proxy and SSL times, but no DNS times. |
| TEST_F(URLRequestLoadTimingTest, InterceptLoadTimingProxy) { |
| base::TimeTicks now = base::TimeTicks::Now(); |
| LoadTimingInfo job_load_timing = |
| NormalLoadTimingInfo(now, CONNECT_TIMING_HAS_SSL_TIMES, true); |
| |
| LoadTimingInfo load_timing_result = RunURLRequestInterceptorLoadTimingTest( |
| job_load_timing, default_context(), interceptor()); |
| |
| // Nothing should have been changed by the URLRequest. |
| EXPECT_EQ(job_load_timing.proxy_resolve_start, |
| load_timing_result.proxy_resolve_start); |
| EXPECT_EQ(job_load_timing.proxy_resolve_end, |
| load_timing_result.proxy_resolve_end); |
| EXPECT_EQ(job_load_timing.connect_timing.domain_lookup_start, |
| load_timing_result.connect_timing.domain_lookup_start); |
| EXPECT_EQ(job_load_timing.connect_timing.domain_lookup_end, |
| load_timing_result.connect_timing.domain_lookup_end); |
| EXPECT_EQ(job_load_timing.connect_timing.connect_start, |
| load_timing_result.connect_timing.connect_start); |
| EXPECT_EQ(job_load_timing.connect_timing.connect_end, |
| load_timing_result.connect_timing.connect_end); |
| EXPECT_EQ(job_load_timing.connect_timing.ssl_start, |
| load_timing_result.connect_timing.ssl_start); |
| EXPECT_EQ(job_load_timing.connect_timing.ssl_end, |
| load_timing_result.connect_timing.ssl_end); |
| |
| // Redundant sanity check. |
| TestLoadTimingNotReusedWithProxy(load_timing_result, |
| CONNECT_TIMING_HAS_SSL_TIMES); |
| } |
| |
| // Make sure that URLRequest correctly adjusts proxy times when they're before |
| // |request_start|, due to already having a connected socket. This happens in |
| // the case of reusing a SPDY session. The connected socket is not considered |
| // reused in this test (May be a preconnect). |
| // |
| // To mix things up from the test above, assumes DNS times but no SSL times. |
| TEST_F(URLRequestLoadTimingTest, InterceptLoadTimingEarlyProxyResolution) { |
| base::TimeTicks now = base::TimeTicks::Now(); |
| LoadTimingInfo job_load_timing = |
| NormalLoadTimingInfo(now, CONNECT_TIMING_HAS_DNS_TIMES, true); |
| job_load_timing.proxy_resolve_start = now - base::Days(6); |
| job_load_timing.proxy_resolve_end = now - base::Days(5); |
| job_load_timing.connect_timing.domain_lookup_start = now - base::Days(4); |
| job_load_timing.connect_timing.domain_lookup_end = now - base::Days(3); |
| job_load_timing.connect_timing.connect_start = now - base::Days(2); |
| job_load_timing.connect_timing.connect_end = now - base::Days(1); |
| |
| LoadTimingInfo load_timing_result = RunURLRequestInterceptorLoadTimingTest( |
| job_load_timing, default_context(), interceptor()); |
| |
| // Proxy times, connect times, and DNS times should all be replaced with |
| // request_start. |
| EXPECT_EQ(load_timing_result.request_start, |
| load_timing_result.proxy_resolve_start); |
| EXPECT_EQ(load_timing_result.request_start, |
| load_timing_result.proxy_resolve_end); |
| EXPECT_EQ(load_timing_result.request_start, |
| load_timing_result.connect_timing.domain_lookup_start); |
| EXPECT_EQ(load_timing_result.request_start, |
| load_timing_result.connect_timing.domain_lookup_end); |
| EXPECT_EQ(load_timing_result.request_start, |
| load_timing_result.connect_timing.connect_start); |
| EXPECT_EQ(load_timing_result.request_start, |
| load_timing_result.connect_timing.connect_end); |
| |
| // Other times should have been left null. |
| TestLoadTimingNotReusedWithProxy(load_timing_result, |
| CONNECT_TIMING_HAS_DNS_TIMES); |
| } |
| |
| // Same as above, but in the reused case. |
| TEST_F(URLRequestLoadTimingTest, |
| InterceptLoadTimingEarlyProxyResolutionReused) { |
| base::TimeTicks now = base::TimeTicks::Now(); |
| LoadTimingInfo job_load_timing = NormalLoadTimingInfoReused(now, true); |
| job_load_timing.proxy_resolve_start = now - base::Days(4); |
| job_load_timing.proxy_resolve_end = now - base::Days(3); |
| |
| LoadTimingInfo load_timing_result = RunURLRequestInterceptorLoadTimingTest( |
| job_load_timing, default_context(), interceptor()); |
| |
| // Proxy times and connect times should all be replaced with request_start. |
| EXPECT_EQ(load_timing_result.request_start, |
| load_timing_result.proxy_resolve_start); |
| EXPECT_EQ(load_timing_result.request_start, |
| load_timing_result.proxy_resolve_end); |
| |
| // Other times should have been left null. |
| TestLoadTimingReusedWithProxy(load_timing_result); |
| } |
| |
| // Make sure that URLRequest correctly adjusts connect times when they're before |
| // |request_start|, due to reusing a connected socket. The connected socket is |
| // not considered reused in this test (May be a preconnect). |
| // |
| // To mix things up, the request has SSL times, but no DNS times. |
| TEST_F(URLRequestLoadTimingTest, InterceptLoadTimingEarlyConnect) { |
| base::TimeTicks now = base::TimeTicks::Now(); |
| LoadTimingInfo job_load_timing = |
| NormalLoadTimingInfo(now, CONNECT_TIMING_HAS_SSL_TIMES, false); |
| job_load_timing.connect_timing.connect_start = now - base::Days(1); |
| job_load_timing.connect_timing.ssl_start = now - base::Days(2); |
| job_load_timing.connect_timing.ssl_end = now - base::Days(3); |
| job_load_timing.connect_timing.connect_end = now - base::Days(4); |
| |
| LoadTimingInfo load_timing_result = RunURLRequestInterceptorLoadTimingTest( |
| job_load_timing, default_context(), interceptor()); |
| |
| // Connect times, and SSL times should be replaced with request_start. |
| EXPECT_EQ(load_timing_result.request_start, |
| load_timing_result.connect_timing.connect_start); |
| EXPECT_EQ(load_timing_result.request_start, |
| load_timing_result.connect_timing.ssl_start); |
| EXPECT_EQ(load_timing_result.request_start, |
| load_timing_result.connect_timing.ssl_end); |
| EXPECT_EQ(load_timing_result.request_start, |
| load_timing_result.connect_timing.connect_end); |
| |
| // Other times should have been left null. |
| TestLoadTimingNotReused(load_timing_result, CONNECT_TIMING_HAS_SSL_TIMES); |
| } |
| |
| // Make sure that URLRequest correctly adjusts connect times when they're before |
| // |request_start|, due to reusing a connected socket in the case that there |
| // are also proxy times. The connected socket is not considered reused in this |
| // test (May be a preconnect). |
| // |
| // In this test, there are no SSL or DNS times. |
| TEST_F(URLRequestLoadTimingTest, InterceptLoadTimingEarlyConnectWithProxy) { |
| base::TimeTicks now = base::TimeTicks::Now(); |
| LoadTimingInfo job_load_timing = |
| NormalLoadTimingInfo(now, CONNECT_TIMING_HAS_CONNECT_TIMES_ONLY, true); |
| job_load_timing.connect_timing.connect_start = now - base::Days(1); |
| job_load_timing.connect_timing.connect_end = now - base::Days(2); |
| |
| LoadTimingInfo load_timing_result = RunURLRequestInterceptorLoadTimingTest( |
| job_load_timing, default_context(), interceptor()); |
| |
| // Connect times should be replaced with proxy_resolve_end. |
| EXPECT_EQ(load_timing_result.proxy_resolve_end, |
| load_timing_result.connect_timing.connect_start); |
| EXPECT_EQ(load_timing_result.proxy_resolve_end, |
| load_timing_result.connect_timing.connect_end); |
| |
| // Other times should have been left null. |
| TestLoadTimingNotReusedWithProxy(load_timing_result, |
| CONNECT_TIMING_HAS_CONNECT_TIMES_ONLY); |
| } |
| |
| TEST_F(URLRequestTest, NetworkDelegateProxyError) { |
| auto context_builder = CreateTestURLRequestContextBuilder(); |
| context_builder->set_proxy_resolution_service( |
| CreateFixedProxyResolutionService("myproxy:70")); |
| auto& network_delegate = *context_builder->set_network_delegate( |
| std::make_unique<TestNetworkDelegate>()); |
| auto host_resolver = std::make_unique<MockHostResolver>(); |
| host_resolver->rules()->AddSimulatedTimeoutFailure("*"); |
| context_builder->set_host_resolver(std::move(host_resolver)); |
| auto context = context_builder->Build(); |
| |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req( |
| context->CreateRequest(GURL("http://example.com"), DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->set_method("GET"); |
| |
| req->Start(); |
| d.RunUntilComplete(); |
| |
| // Check we see a failed request. |
| // The proxy server should be set before failure. |
| EXPECT_EQ(PacResultElementToProxyServer("PROXY myproxy:70"), |
| req->proxy_server()); |
| EXPECT_EQ(ERR_PROXY_CONNECTION_FAILED, d.request_status()); |
| EXPECT_THAT(req->response_info().resolve_error_info.error, |
| IsError(ERR_DNS_TIMED_OUT)); |
| |
| EXPECT_EQ(1, network_delegate.error_count()); |
| EXPECT_THAT(network_delegate.last_error(), |
| IsError(ERR_PROXY_CONNECTION_FAILED)); |
| EXPECT_EQ(1, network_delegate.completed_requests()); |
| } |
| |
| // Test that when host resolution fails with `ERR_DNS_NAME_HTTPS_ONLY` for |
| // "http://" requests, scheme is upgraded to "https://". |
| TEST_F(URLRequestTest, DnsNameHttpsOnlyErrorCausesSchemeUpgrade) { |
| EmbeddedTestServer https_server(EmbeddedTestServer::TYPE_HTTPS); |
| https_server.SetSSLConfig(EmbeddedTestServer::CERT_TEST_NAMES); |
| RegisterDefaultHandlers(&https_server); |
| ASSERT_TRUE(https_server.Start()); |
| |
| // Build an http URL that should be auto-upgraded to https. |
| const std::string kHost = "foo.a.test"; // Covered by CERT_TEST_NAMES. |
| const GURL https_url = https_server.GetURL(kHost, "/defaultresponse"); |
| GURL::Replacements replacements; |
| replacements.SetSchemeStr(url::kHttpScheme); |
| const GURL http_url = https_url.ReplaceComponents(replacements); |
| |
| // Return `ERR_DNS_NAME_HTTPS_ONLY` for "http://" requests and an address for |
| // "https://" requests. This simulates the HostResolver behavior for a domain |
| // with an HTTPS DNS record. |
| auto host_resolver = std::make_unique<MockHostResolver>(); |
| MockHostResolverBase::RuleResolver::RuleKey unencrypted_resolve_key; |
| unencrypted_resolve_key.scheme = url::kHttpScheme; |
| unencrypted_resolve_key.hostname_pattern = kHost; |
| host_resolver->rules()->AddRule(std::move(unencrypted_resolve_key), |
| ERR_DNS_NAME_HTTPS_ONLY); |
| MockHostResolverBase::RuleResolver::RuleKey encrypted_resolve_key; |
| encrypted_resolve_key.scheme = url::kHttpsScheme; |
| encrypted_resolve_key.hostname_pattern = kHost; |
| host_resolver->rules()->AddRule(std::move(encrypted_resolve_key), |
| https_server.GetIPLiteralString()); |
| auto context_builder = CreateTestURLRequestContextBuilder(); |
| context_builder->set_host_resolver(std::move(host_resolver)); |
| auto& network_delegate = *context_builder->set_network_delegate( |
| std::make_unique<TestNetworkDelegate>()); |
| auto context = context_builder->Build(); |
| |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req(context->CreateRequest( |
| http_url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| EXPECT_FALSE(req->url().SchemeIsCryptographic()); |
| |
| // Note that there is no http server running, so the request should fail or |
| // hang if its scheme is not upgraded to https. |
| req->Start(); |
| d.RunUntilComplete(); |
| |
| EXPECT_EQ(d.received_redirect_count(), 1); |
| |
| EXPECT_EQ(0, network_delegate.error_count()); |
| EXPECT_EQ(200, req->GetResponseCode()); |
| ASSERT_TRUE(req->response_headers()); |
| EXPECT_EQ(200, req->response_headers()->response_code()); |
| |
| // Observe that the scheme has been upgraded to https. |
| EXPECT_TRUE(req->url().SchemeIsCryptographic()); |
| EXPECT_TRUE(req->url().SchemeIs(url::kHttpsScheme)); |
| } |
| |
| // Test that DNS-based scheme upgrade supports deferred redirect. |
| TEST_F(URLRequestTest, DnsNameHttpsOnlyErrorCausesSchemeUpgradeDeferred) { |
| EmbeddedTestServer https_server(EmbeddedTestServer::TYPE_HTTPS); |
| https_server.SetSSLConfig(EmbeddedTestServer::CERT_TEST_NAMES); |
| RegisterDefaultHandlers(&https_server); |
| ASSERT_TRUE(https_server.Start()); |
| |
| // Build an http URL that should be auto-upgraded to https. |
| const std::string kHost = "foo.a.test"; // Covered by CERT_TEST_NAMES. |
| const GURL https_url = https_server.GetURL(kHost, "/defaultresponse"); |
| GURL::Replacements replacements; |
| replacements.SetSchemeStr(url::kHttpScheme); |
| const GURL http_url = https_url.ReplaceComponents(replacements); |
| |
| // Return `ERR_DNS_NAME_HTTPS_ONLY` for "http://" requests and an address for |
| // "https://" requests. This simulates the HostResolver behavior for a domain |
| // with an HTTPS DNS record. |
| auto host_resolver = std::make_unique<MockHostResolver>(); |
| MockHostResolverBase::RuleResolver::RuleKey unencrypted_resolve_key; |
| unencrypted_resolve_key.scheme = url::kHttpScheme; |
| unencrypted_resolve_key.hostname_pattern = kHost; |
| host_resolver->rules()->AddRule(std::move(unencrypted_resolve_key), |
| ERR_DNS_NAME_HTTPS_ONLY); |
| MockHostResolverBase::RuleResolver::RuleKey encrypted_resolve_key; |
| encrypted_resolve_key.scheme = url::kHttpsScheme; |
| encrypted_resolve_key.hostname_pattern = kHost; |
| host_resolver->rules()->AddRule(std::move(encrypted_resolve_key), |
| https_server.GetIPLiteralString()); |
| auto context_builder = CreateTestURLRequestContextBuilder(); |
| context_builder->set_host_resolver(std::move(host_resolver)); |
| auto& network_delegate = *context_builder->set_network_delegate( |
| std::make_unique<TestNetworkDelegate>()); |
| auto context = context_builder->Build(); |
| |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req(context->CreateRequest( |
| http_url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| EXPECT_FALSE(req->url().SchemeIsCryptographic()); |
| |
| // Note that there is no http server running, so the request should fail or |
| // hang if its scheme is not upgraded to https. |
| req->Start(); |
| d.RunUntilRedirect(); |
| |
| EXPECT_EQ(d.received_redirect_count(), 1); |
| |
| req->FollowDeferredRedirect(/*removed_headers=*/absl::nullopt, |
| /*modified_headers=*/absl::nullopt); |
| d.RunUntilComplete(); |
| |
| EXPECT_EQ(0, network_delegate.error_count()); |
| EXPECT_EQ(200, req->GetResponseCode()); |
| ASSERT_TRUE(req->response_headers()); |
| EXPECT_EQ(200, req->response_headers()->response_code()); |
| |
| // Observe that the scheme has been upgraded to https. |
| EXPECT_TRUE(req->url().SchemeIsCryptographic()); |
| EXPECT_TRUE(req->url().SchemeIs(url::kHttpsScheme)); |
| } |
| |
| #if BUILDFLAG(ENABLE_WEBSOCKETS) |
| // Test that requests with "ws" scheme are upgraded to "wss" when DNS |
| // indicates that the name is HTTPS-only. |
| TEST_F(URLRequestTest, DnsHttpsRecordPresentCausesWsSchemeUpgrade) { |
| EmbeddedTestServer https_server(EmbeddedTestServer::TYPE_HTTPS); |
| https_server.SetSSLConfig(EmbeddedTestServer::CERT_TEST_NAMES); |
| RegisterDefaultHandlers(&https_server); |
| ASSERT_TRUE(https_server.Start()); |
| |
| // Build an http URL that should be auto-upgraded to https. |
| const std::string kHost = "foo.a.test"; // Covered by CERT_TEST_NAMES. |
| const GURL https_url = https_server.GetURL(kHost, "/defaultresponse"); |
| GURL::Replacements replacements; |
| replacements.SetSchemeStr(url::kWsScheme); |
| const GURL ws_url = https_url.ReplaceComponents(replacements); |
| |
| auto host_resolver = std::make_unique<MockHostResolver>(); |
| MockHostResolverBase::RuleResolver::RuleKey unencrypted_resolve_key; |
| unencrypted_resolve_key.scheme = url::kHttpScheme; |
| unencrypted_resolve_key.hostname_pattern = kHost; |
| host_resolver->rules()->AddRule(std::move(unencrypted_resolve_key), |
| ERR_DNS_NAME_HTTPS_ONLY); |
| MockHostResolverBase::RuleResolver::RuleKey encrypted_resolve_key; |
| encrypted_resolve_key.scheme = url::kHttpsScheme; |
| encrypted_resolve_key.hostname_pattern = kHost; |
| host_resolver->rules()->AddRule(std::move(encrypted_resolve_key), |
| https_server.GetIPLiteralString()); |
| auto context_builder = CreateTestURLRequestContextBuilder(); |
| context_builder->set_host_resolver(std::move(host_resolver)); |
| auto& network_delegate = *context_builder->set_network_delegate( |
| std::make_unique<TestNetworkDelegate>()); |
| auto context = context_builder->Build(); |
| |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req(context->CreateRequest( |
| ws_url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS, |
| /*is_for_websockets=*/true)); |
| EXPECT_FALSE(req->url().SchemeIsCryptographic()); |
| |
| HttpRequestHeaders headers = WebSocketCommonTestHeaders(); |
| req->SetExtraRequestHeaders(headers); |
| |
| auto websocket_stream_create_helper = |
| std::make_unique<TestWebSocketHandshakeStreamCreateHelper>(); |
| req->SetUserData(kWebSocketHandshakeUserDataKey, |
| std::move(websocket_stream_create_helper)); |
| |
| // Note that there is no ws server running, so the request should fail or hang |
| // if its scheme is not upgraded to wss. |
| req->Start(); |
| d.RunUntilComplete(); |
| |
| EXPECT_EQ(d.received_redirect_count(), 1); |
| |
| // Expect failure because test server is not set up to provide websocket |
| // responses. |
| EXPECT_EQ(network_delegate.error_count(), 1); |
| EXPECT_EQ(network_delegate.last_error(), ERR_INVALID_RESPONSE); |
| |
| // Observe that the scheme has been upgraded to wss. |
| EXPECT_TRUE(req->url().SchemeIsCryptographic()); |
| EXPECT_TRUE(req->url().SchemeIs(url::kWssScheme)); |
| } |
| #endif // BUILDFLAG(ENABLE_WEBSOCKETS) |
| |
| TEST_F(URLRequestTest, DnsHttpsRecordAbsentNoSchemeUpgrade) { |
| EmbeddedTestServer http_server(EmbeddedTestServer::TYPE_HTTP); |
| RegisterDefaultHandlers(&http_server); |
| ASSERT_TRUE(http_server.Start()); |
| |
| // Build an http URL that should be auto-upgraded to https. |
| const std::string kHost = "foo.a.test"; // Covered by CERT_TEST_NAMES. |
| const GURL http_url = http_server.GetURL(kHost, "/defaultresponse"); |
| |
| auto context_builder = CreateTestURLRequestContextBuilder(); |
| auto host_resolver = std::make_unique<MockHostResolver>(); |
| host_resolver->rules()->AddRule(kHost, http_server.GetIPLiteralString()); |
| context_builder->set_host_resolver(std::move(host_resolver)); |
| auto& network_delegate = *context_builder->set_network_delegate( |
| std::make_unique<TestNetworkDelegate>()); |
| auto context = context_builder->Build(); |
| |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req(context->CreateRequest( |
| http_url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| EXPECT_FALSE(req->url().SchemeIsCryptographic()); |
| |
| req->Start(); |
| d.RunUntilComplete(); |
| |
| EXPECT_EQ(d.received_redirect_count(), 0); |
| |
| EXPECT_EQ(0, network_delegate.error_count()); |
| EXPECT_EQ(200, req->GetResponseCode()); |
| ASSERT_TRUE(req->response_headers()); |
| EXPECT_EQ(200, req->response_headers()->response_code()); |
| |
| // Observe that the scheme has not been upgraded. |
| EXPECT_EQ(http_url, req->url()); |
| EXPECT_FALSE(req->url().SchemeIsCryptographic()); |
| EXPECT_TRUE(req->url().SchemeIs(url::kHttpScheme)); |
| } |
| |
| TEST_F(URLRequestTest, SkipSecureDnsDisabledByDefault) { |
| auto context_builder = CreateTestURLRequestContextBuilder(); |
| auto host_resolver = std::make_unique<MockHostResolver>(); |
| host_resolver->rules()->AddRule("example.com", "127.0.0.1"); |
| context_builder->set_host_resolver(std::move(host_resolver)); |
| auto context = context_builder->Build(); |
| |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req( |
| context->CreateRequest(GURL("http://example.com"), DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->Start(); |
| d.RunUntilComplete(); |
| |
| EXPECT_EQ(SecureDnsPolicy::kAllow, |
| static_cast<MockHostResolver*>(context->host_resolver()) |
| ->last_secure_dns_policy()); |
| } |
| |
| TEST_F(URLRequestTest, SkipSecureDnsEnabled) { |
| auto context_builder = CreateTestURLRequestContextBuilder(); |
| auto host_resolver = std::make_unique<MockHostResolver>(); |
| host_resolver->rules()->AddRule("example.com", "127.0.0.1"); |
| context_builder->set_host_resolver(std::move(host_resolver)); |
| auto context = context_builder->Build(); |
| |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req( |
| context->CreateRequest(GURL("http://example.com"), DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->SetSecureDnsPolicy(SecureDnsPolicy::kDisable); |
| req->Start(); |
| d.RunUntilComplete(); |
| |
| EXPECT_EQ(SecureDnsPolicy::kDisable, |
| static_cast<MockHostResolver*>(context->host_resolver()) |
| ->last_secure_dns_policy()); |
| } |
| |
| // Make sure that NetworkDelegate::NotifyCompleted is called if |
| // content is empty. |
| TEST_F(URLRequestTest, RequestCompletionForEmptyResponse) { |
| HttpTestServer test_server; |
| ASSERT_TRUE(test_server.Start()); |
| |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req(default_context().CreateRequest( |
| test_server.GetURL("/nocontent"), DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->Start(); |
| d.RunUntilComplete(); |
| EXPECT_THAT(d.request_status(), IsOk()); |
| EXPECT_EQ(204, req->GetResponseCode()); |
| EXPECT_EQ("", d.data_received()); |
| EXPECT_EQ(1, default_network_delegate().completed_requests()); |
| } |
| |
| // Make sure that SetPriority actually sets the URLRequest's priority |
| // correctly, both before and after start. |
| TEST_F(URLRequestTest, SetPriorityBasic) { |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req(default_context().CreateRequest( |
| GURL("http://test_intercept/foo"), DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| EXPECT_EQ(DEFAULT_PRIORITY, req->priority()); |
| |
| req->SetPriority(LOW); |
| EXPECT_EQ(LOW, req->priority()); |
| |
| req->Start(); |
| EXPECT_EQ(LOW, req->priority()); |
| |
| req->SetPriority(MEDIUM); |
| EXPECT_EQ(MEDIUM, req->priority()); |
| } |
| |
| // Make sure that URLRequest calls SetPriority on a job before calling |
| // Start on it. |
| TEST_F(URLRequestTest, SetJobPriorityBeforeJobStart) { |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req(default_context().CreateRequest( |
| GURL("http://test_intercept/foo"), DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| EXPECT_EQ(DEFAULT_PRIORITY, req->priority()); |
| |
| RequestPriority job_priority; |
| std::unique_ptr<URLRequestJob> job = |
| std::make_unique<PriorityMonitoringURLRequestJob>(req.get(), |
| &job_priority); |
| TestScopedURLInterceptor interceptor(req->url(), std::move(job)); |
| EXPECT_EQ(DEFAULT_PRIORITY, job_priority); |
| |
| req->SetPriority(LOW); |
| |
| req->Start(); |
| EXPECT_EQ(LOW, job_priority); |
| } |
| |
| // Make sure that URLRequest passes on its priority updates to its |
| // job. |
| TEST_F(URLRequestTest, SetJobPriority) { |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req(default_context().CreateRequest( |
| GURL("http://test_intercept/foo"), DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| |
| RequestPriority job_priority; |
| std::unique_ptr<URLRequestJob> job = |
| std::make_unique<PriorityMonitoringURLRequestJob>(req.get(), |
| &job_priority); |
| TestScopedURLInterceptor interceptor(req->url(), std::move(job)); |
| |
| req->SetPriority(LOW); |
| req->Start(); |
| EXPECT_EQ(LOW, job_priority); |
| |
| req->SetPriority(MEDIUM); |
| EXPECT_EQ(MEDIUM, req->priority()); |
| EXPECT_EQ(MEDIUM, job_priority); |
| } |
| |
| // Setting the IGNORE_LIMITS load flag should be okay if the priority |
| // is MAXIMUM_PRIORITY. |
| TEST_F(URLRequestTest, PriorityIgnoreLimits) { |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req(default_context().CreateRequest( |
| GURL("http://test_intercept/foo"), MAXIMUM_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| EXPECT_EQ(MAXIMUM_PRIORITY, req->priority()); |
| |
| RequestPriority job_priority; |
| std::unique_ptr<URLRequestJob> job = |
| std::make_unique<PriorityMonitoringURLRequestJob>(req.get(), |
| &job_priority); |
| TestScopedURLInterceptor interceptor(req->url(), std::move(job)); |
| |
| req->SetLoadFlags(LOAD_IGNORE_LIMITS); |
| EXPECT_EQ(MAXIMUM_PRIORITY, req->priority()); |
| |
| req->SetPriority(MAXIMUM_PRIORITY); |
| EXPECT_EQ(MAXIMUM_PRIORITY, req->priority()); |
| |
| req->Start(); |
| EXPECT_EQ(MAXIMUM_PRIORITY, req->priority()); |
| EXPECT_EQ(MAXIMUM_PRIORITY, job_priority); |
| } |
| |
| // This test verifies that URLRequest::Delegate's OnConnected() callback is |
| // never called if the request fails before connecting to a remote endpoint. |
| TEST_F(URLRequestTest, NotifyDelegateConnectedSkippedOnEarlyFailure) { |
| TestDelegate delegate; |
| |
| // The request will never connect to anything because the URL is invalid. |
| auto request = |
| default_context().CreateRequest(GURL("invalid url"), DEFAULT_PRIORITY, |
| &delegate, TRAFFIC_ANNOTATION_FOR_TESTS); |
| |
| request->Start(); |
| delegate.RunUntilComplete(); |
| |
| EXPECT_THAT(delegate.transports(), IsEmpty()); |
| } |
| |
| // This test verifies that URLRequest::Delegate's OnConnected() method |
| // is called once for simple redirect-less requests. |
| TEST_F(URLRequestTest, OnConnected) { |
| HttpTestServer test_server; |
| ASSERT_TRUE(test_server.Start()); |
| |
| TestDelegate delegate; |
| |
| auto request = default_context().CreateRequest(test_server.GetURL("/echo"), |
| DEFAULT_PRIORITY, &delegate, |
| TRAFFIC_ANNOTATION_FOR_TESTS); |
| |
| request->Start(); |
| delegate.RunUntilComplete(); |
| |
| TransportInfo expected_transport; |
| expected_transport.endpoint = |
| IPEndPoint(IPAddress::IPv4Localhost(), test_server.port()); |
| EXPECT_THAT(delegate.transports(), ElementsAre(expected_transport)); |
| |
| // Make sure URL_REQUEST_DELEGATE_CONNECTED is logged correctly. |
| auto entries = net_log_observer_.GetEntries(); |
| size_t start_event_index = ExpectLogContainsSomewhere( |
| entries, /*min_offset=*/0, |
| NetLogEventType::URL_REQUEST_DELEGATE_CONNECTED, NetLogEventPhase::BEGIN); |
| size_t end_event_index = ExpectLogContainsSomewhereAfter( |
| entries, /*start_offset=*/start_event_index, |
| NetLogEventType::URL_REQUEST_DELEGATE_CONNECTED, NetLogEventPhase::END); |
| EXPECT_FALSE(LogContainsEntryWithTypeAfter( |
| entries, end_event_index + 1, |
| NetLogEventType::URL_REQUEST_DELEGATE_CONNECTED)); |
| ASSERT_LT(end_event_index, entries.size()); |
| EXPECT_FALSE(GetOptionalNetErrorCodeFromParams(entries[end_event_index])); |
| } |
| |
| // This test verifies that URLRequest::Delegate's OnConnected() method is |
| // called after each redirect. |
| TEST_F(URLRequestTest, OnConnectedRedirect) { |
| HttpTestServer test_server; |
| ASSERT_TRUE(test_server.Start()); |
| |
| TestDelegate delegate; |
| |
| // Fetch a page that redirects us once. |
| GURL url = test_server.GetURL("/server-redirect?" + |
| test_server.GetURL("/echo").spec()); |
| auto request = default_context().CreateRequest( |
| url, DEFAULT_PRIORITY, &delegate, TRAFFIC_ANNOTATION_FOR_TESTS); |
| |
| request->Start(); |
| delegate.RunUntilRedirect(); |
| |
| TransportInfo expected_transport; |
| expected_transport.endpoint = |
| IPEndPoint(IPAddress::IPv4Localhost(), test_server.port()); |
| EXPECT_THAT(delegate.transports(), ElementsAre(expected_transport)); |
| |
| request->FollowDeferredRedirect(/*removed_headers=*/{}, |
| /*modified_headers=*/{}); |
| delegate.RunUntilComplete(); |
| |
| EXPECT_THAT(delegate.transports(), |
| ElementsAre(expected_transport, expected_transport)); |
| } |
| |
| // This test verifies that when the URLRequest Delegate returns an error from |
| // OnConnected(), the entire request fails with that error. |
| TEST_F(URLRequestTest, OnConnectedError) { |
| HttpTestServer test_server; |
| ASSERT_TRUE(test_server.Start()); |
| |
| TestDelegate delegate; |
| delegate.set_on_connected_result(ERR_NOT_IMPLEMENTED); |
| |
| auto request = default_context().CreateRequest(test_server.GetURL("/echo"), |
| DEFAULT_PRIORITY, &delegate, |
| TRAFFIC_ANNOTATION_FOR_TESTS); |
| |
| request->Start(); |
| delegate.RunUntilComplete(); |
| |
| TransportInfo expected_transport; |
| expected_transport.endpoint = |
| IPEndPoint(IPAddress::IPv4Localhost(), test_server.port()); |
| EXPECT_THAT(delegate.transports(), ElementsAre(expected_transport)); |
| |
| EXPECT_TRUE(delegate.request_failed()); |
| EXPECT_THAT(delegate.request_status(), IsError(ERR_NOT_IMPLEMENTED)); |
| |
| // Make sure URL_REQUEST_DELEGATE_CONNECTED is logged correctly. |
| auto entries = net_log_observer_.GetEntries(); |
| size_t start_event_index = ExpectLogContainsSomewhere( |
| entries, /*min_offset=*/0, |
| NetLogEventType::URL_REQUEST_DELEGATE_CONNECTED, NetLogEventPhase::BEGIN); |
| size_t end_event_index = ExpectLogContainsSomewhereAfter( |
| entries, /*start_offset=*/start_event_index, |
| NetLogEventType::URL_REQUEST_DELEGATE_CONNECTED, NetLogEventPhase::END); |
| EXPECT_FALSE(LogContainsEntryWithTypeAfter( |
| entries, end_event_index + 1, |
| NetLogEventType::URL_REQUEST_DELEGATE_CONNECTED)); |
| ASSERT_LT(end_event_index, entries.size()); |
| EXPECT_EQ(ERR_NOT_IMPLEMENTED, |
| GetOptionalNetErrorCodeFromParams(entries[end_event_index])); |
| } |
| |
| TEST_F(URLRequestTest, OnConnectedAsync) { |
| HttpTestServer test_server; |
| ASSERT_TRUE(test_server.Start()); |
| |
| TestDelegate d; |
| d.set_on_connected_run_callback(true); |
| d.set_on_connected_result(OK); |
| std::unique_ptr<URLRequest> req = CreateFirstPartyRequest( |
| default_context(), test_server.GetURL("/defaultresponse"), &d); |
| req->Start(); |
| d.RunUntilComplete(); |
| EXPECT_THAT(d.request_status(), IsOk()); |
| |
| // Make sure URL_REQUEST_DELEGATE_CONNECTED is logged correctly. |
| auto entries = net_log_observer_.GetEntries(); |
| size_t start_event_index = ExpectLogContainsSomewhere( |
| entries, /*min_offset=*/0, |
| NetLogEventType::URL_REQUEST_DELEGATE_CONNECTED, NetLogEventPhase::BEGIN); |
| size_t end_event_index = ExpectLogContainsSomewhereAfter( |
| entries, /*start_offset=*/start_event_index, |
| NetLogEventType::URL_REQUEST_DELEGATE_CONNECTED, NetLogEventPhase::END); |
| EXPECT_FALSE(LogContainsEntryWithTypeAfter( |
| entries, end_event_index + 1, |
| NetLogEventType::URL_REQUEST_DELEGATE_CONNECTED)); |
| ASSERT_LT(end_event_index, entries.size()); |
| EXPECT_FALSE(GetOptionalNetErrorCodeFromParams(entries[end_event_index])); |
| } |
| |
| TEST_F(URLRequestTest, OnConnectedAsyncError) { |
| HttpTestServer test_server; |
| ASSERT_TRUE(test_server.Start()); |
| |
| TestDelegate d; |
| d.set_on_connected_run_callback(true); |
| d.set_on_connected_result(ERR_NOT_IMPLEMENTED); |
| std::unique_ptr<URLRequest> req = CreateFirstPartyRequest( |
| default_context(), test_server.GetURL("/defaultresponse"), &d); |
| req->Start(); |
| d.RunUntilComplete(); |
| EXPECT_THAT(d.request_status(), IsError(ERR_NOT_IMPLEMENTED)); |
| |
| // Make sure URL_REQUEST_DELEGATE_CONNECTED is logged correctly. |
| auto entries = net_log_observer_.GetEntries(); |
| size_t start_event_index = ExpectLogContainsSomewhere( |
| entries, /*min_offset=*/0, |
| NetLogEventType::URL_REQUEST_DELEGATE_CONNECTED, NetLogEventPhase::BEGIN); |
| size_t end_event_index = ExpectLogContainsSomewhereAfter( |
| entries, /*start_offset=*/start_event_index, |
| NetLogEventType::URL_REQUEST_DELEGATE_CONNECTED, NetLogEventPhase::END); |
| EXPECT_FALSE(LogContainsEntryWithTypeAfter( |
| entries, end_event_index + 1, |
| NetLogEventType::URL_REQUEST_DELEGATE_CONNECTED)); |
| ASSERT_LT(end_event_index, entries.size()); |
| EXPECT_EQ(ERR_NOT_IMPLEMENTED, |
| GetOptionalNetErrorCodeFromParams(entries[end_event_index])); |
| } |
| |
| TEST_F(URLRequestTest, DelayedCookieCallback) { |
| HttpTestServer test_server; |
| ASSERT_TRUE(test_server.Start()); |
| |
| auto context_builder = CreateTestURLRequestContextBuilder(); |
| context_builder->SetCookieStore(std::make_unique<DelayedCookieMonster>()); |
| auto& network_delegate = *context_builder->set_network_delegate( |
| std::make_unique<TestNetworkDelegate>()); |
| auto context = context_builder->Build(); |
| |
| // Set up a cookie. |
| { |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req = CreateFirstPartyRequest( |
| *context, test_server.GetURL("/set-cookie?CookieToNotSend=1"), &d); |
| req->Start(); |
| d.RunUntilComplete(); |
| EXPECT_EQ(0, network_delegate.blocked_annotate_cookies_count()); |
| EXPECT_EQ(0, network_delegate.blocked_set_cookie_count()); |
| EXPECT_EQ(1, network_delegate.set_cookie_count()); |
| } |
| |
| // Verify that the cookie is set. |
| { |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req = CreateFirstPartyRequest( |
| *context, test_server.GetURL("/echoheader?Cookie"), &d); |
| req->Start(); |
| d.RunUntilComplete(); |
| |
| EXPECT_TRUE(d.data_received().find("CookieToNotSend=1") != |
| std::string::npos); |
| EXPECT_EQ(0, network_delegate.blocked_annotate_cookies_count()); |
| EXPECT_EQ(0, network_delegate.blocked_set_cookie_count()); |
| } |
| } |
| |
| TEST_F(URLRequestTest, DelayedCookieCallbackAsync) { |
| HttpTestServer test_server; |
| ASSERT_TRUE(test_server.Start()); |
| |
| // Add a secure cookie so we can try to set an insecure cookie and have |
| // SetCanonicalCookie fail. |
| GURL::Replacements replace_scheme; |
| replace_scheme.SetSchemeStr("https"); |
| GURL url = test_server.base_url().ReplaceComponents(replace_scheme); |
| |
| auto cookie1 = CanonicalCookie::Create( |
| url, "AlreadySetCookie=1;Secure", base::Time::Now(), |
| absl::nullopt /* server_time */, |
| absl::nullopt /* cookie_partition_key */); |
| auto delayed_cm = std::make_unique<DelayedCookieMonster>(); |
| delayed_cm->SetCanonicalCookieAsync(std::move(cookie1), url, |
| net::CookieOptions::MakeAllInclusive(), |
| CookieStore::SetCookiesCallback()); |
| |
| auto cookie2 = CanonicalCookie::Create( |
| url, "AlreadySetCookie=1;Secure", base::Time::Now(), |
| absl::nullopt /* server_time */, |
| absl::nullopt /* cookie_partition_key */); |
| auto cm = std::make_unique<CookieMonster>(nullptr, nullptr); |
| cm->SetCanonicalCookieAsync(std::move(cookie2), url, |
| net::CookieOptions::MakeAllInclusive(), |
| CookieStore::SetCookiesCallback()); |
| |
| auto async_context_builder = CreateTestURLRequestContextBuilder(); |
| async_context_builder->SetCookieStore(std::move(delayed_cm)); |
| auto& async_filter_network_delegate = |
| *async_context_builder->set_network_delegate( |
| std::make_unique<FilteringTestNetworkDelegate>()); |
| auto async_context = async_context_builder->Build(); |
| async_filter_network_delegate.SetCookieFilter("CookieBlockedOnCanGetCookie"); |
| TestDelegate async_delegate; |
| |
| auto sync_context_builder = CreateTestURLRequestContextBuilder(); |
| sync_context_builder->SetCookieStore(std::move(cm)); |
| auto& sync_filter_network_delegate = |
| *sync_context_builder->set_network_delegate( |
| std::make_unique<FilteringTestNetworkDelegate>()); |
| auto sync_context = sync_context_builder->Build(); |
| sync_filter_network_delegate.SetCookieFilter("CookieBlockedOnCanGetCookie"); |
| TestDelegate sync_delegate; |
| |
| std::vector<std::string> cookie_lines( |
| {// Fails in SetCanonicalCookie for trying to set a secure cookie |
| // on an insecure host. |
| "CookieNotSet=1;Secure", |
| // Fail in FilteringTestNetworkDelegate::CanGetCookie. |
| "CookieBlockedOnCanGetCookie=1", |
| // Fails in SetCanonicalCookie for trying to overwrite a secure cookie |
| // with an insecure cookie. |
| "AlreadySetCookie=1", |
| // Succeeds and added cookie to store. Delayed (which makes the callback |
| // run asynchronously) in DelayedCookieMonster. |
| "CookieSet=1"}); |
| |
| for (auto first_cookie_line : cookie_lines) { |
| for (auto second_cookie_line : cookie_lines) { |
| // Run with the delayed cookie monster. |
| std::unique_ptr<URLRequest> request = CreateFirstPartyRequest( |
| *async_context, |
| test_server.GetURL("/set-cookie?" + first_cookie_line + "&" + |
| second_cookie_line), |
| &async_delegate); |
| |
| request->Start(); |
| async_delegate.RunUntilComplete(); |
| EXPECT_THAT(async_delegate.request_status(), IsOk()); |
| |
| // Run with the regular cookie monster. |
| request = CreateFirstPartyRequest( |
| *sync_context, |
| test_server.GetURL("/set-cookie?" + first_cookie_line + "&" + |
| second_cookie_line), |
| &sync_delegate); |
| |
| request->Start(); |
| sync_delegate.RunUntilComplete(); |
| EXPECT_THAT(sync_delegate.request_status(), IsOk()); |
| |
| int expected_set_cookie_count = 0; |
| int expected_blocked_cookie_count = 0; |
| |
| // 2 calls to the delegate's OnCanSetCookie method are expected, even if |
| // the cookies don't end up getting set. |
| expected_set_cookie_count += 2; |
| |
| if (first_cookie_line == "CookieBlockedOnCanGetCookie=1") |
| ++expected_blocked_cookie_count; |
| if (second_cookie_line == "CookieBlockedOnCanGetCookie=1") |
| ++expected_blocked_cookie_count; |
| |
| EXPECT_EQ(expected_set_cookie_count, |
| async_filter_network_delegate.set_cookie_called_count()); |
| EXPECT_EQ(expected_blocked_cookie_count, |
| async_filter_network_delegate.blocked_set_cookie_count()); |
| |
| EXPECT_EQ(expected_set_cookie_count, |
| sync_filter_network_delegate.set_cookie_called_count()); |
| EXPECT_EQ(expected_blocked_cookie_count, |
| sync_filter_network_delegate.blocked_set_cookie_count()); |
| |
| async_filter_network_delegate.ResetSetCookieCalledCount(); |
| async_filter_network_delegate.ResetBlockedSetCookieCount(); |
| |
| sync_filter_network_delegate.ResetSetCookieCalledCount(); |
| sync_filter_network_delegate.ResetBlockedSetCookieCount(); |
| } |
| } |
| } |
| |
| TEST_F(URLRequestTest, DoNotSendCookies) { |
| HttpTestServer test_server; |
| ASSERT_TRUE(test_server.Start()); |
| |
| // Set up a cookie. |
| { |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req = CreateFirstPartyRequest( |
| default_context(), test_server.GetURL("/set-cookie?CookieToNotSend=1"), |
| &d); |
| req->Start(); |
| d.RunUntilComplete(); |
| EXPECT_EQ(0, default_network_delegate().blocked_annotate_cookies_count()); |
| EXPECT_EQ(0, default_network_delegate().blocked_set_cookie_count()); |
| } |
| |
| // Verify that the cookie is set. |
| { |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req = CreateFirstPartyRequest( |
| default_context(), test_server.GetURL("/echoheader?Cookie"), &d); |
| req->Start(); |
| d.RunUntilComplete(); |
| |
| EXPECT_TRUE(d.data_received().find("CookieToNotSend=1") != |
| std::string::npos); |
| EXPECT_EQ(0, default_network_delegate().blocked_annotate_cookies_count()); |
| EXPECT_EQ(0, default_network_delegate().blocked_set_cookie_count()); |
| } |
| |
| // Verify that the cookie isn't sent when credentials are not allowed. |
| { |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req = CreateFirstPartyRequest( |
| default_context(), test_server.GetURL("/echoheader?Cookie"), &d); |
| req->set_allow_credentials(false); |
| req->Start(); |
| d.RunUntilComplete(); |
| |
| EXPECT_TRUE(d.data_received().find("Cookie: CookieToNotSend=1") == |
| std::string::npos); |
| |
| // When credentials are blocked, OnAnnotateAndMoveUserBlockedCookies() is |
| // not invoked. |
| EXPECT_EQ(0, default_network_delegate().blocked_annotate_cookies_count()); |
| EXPECT_EQ(0, default_network_delegate().blocked_set_cookie_count()); |
| } |
| } |
| |
| TEST_F(URLRequestTest, DoNotSaveCookies) { |
| HttpTestServer test_server; |
| ASSERT_TRUE(test_server.Start()); |
| |
| // Set up a cookie. |
| { |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req = CreateFirstPartyRequest( |
| default_context(), |
| test_server.GetURL("/set-cookie?CookieToNotUpdate=2"), &d); |
| req->Start(); |
| d.RunUntilComplete(); |
| |
| EXPECT_EQ(0, default_network_delegate().blocked_annotate_cookies_count()); |
| EXPECT_EQ(0, default_network_delegate().blocked_set_cookie_count()); |
| EXPECT_EQ(1, default_network_delegate().set_cookie_count()); |
| } |
| |
| // Try to set-up another cookie and update the previous cookie. |
| { |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req = CreateFirstPartyRequest( |
| default_context(), |
| test_server.GetURL("/set-cookie?CookieToNotSave=1&CookieToNotUpdate=1"), |
| &d); |
| req->SetLoadFlags(LOAD_DO_NOT_SAVE_COOKIES); |
| req->Start(); |
| |
| d.RunUntilComplete(); |
| |
| // LOAD_DO_NOT_SAVE_COOKIES does not trigger OnSetCookie. |
| EXPECT_EQ(0, default_network_delegate().blocked_annotate_cookies_count()); |
| EXPECT_EQ(0, default_network_delegate().blocked_set_cookie_count()); |
| EXPECT_EQ(1, default_network_delegate().set_cookie_count()); |
| } |
| |
| // Verify the cookies weren't saved or updated. |
| { |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req = CreateFirstPartyRequest( |
| default_context(), test_server.GetURL("/echoheader?Cookie"), &d); |
| req->Start(); |
| d.RunUntilComplete(); |
| |
| EXPECT_TRUE(d.data_received().find("CookieToNotSave=1") == |
| std::string::npos); |
| EXPECT_TRUE(d.data_received().find("CookieToNotUpdate=2") != |
| std::string::npos); |
| |
| EXPECT_EQ(0, default_network_delegate().blocked_annotate_cookies_count()); |
| EXPECT_EQ(0, default_network_delegate().blocked_set_cookie_count()); |
| EXPECT_EQ(1, default_network_delegate().set_cookie_count()); |
| } |
| } |
| |
| TEST_F(URLRequestTest, DoNotSendCookies_ViaPolicy) { |
| HttpTestServer test_server; |
| ASSERT_TRUE(test_server.Start()); |
| |
| // Set up a cookie. |
| { |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req = CreateFirstPartyRequest( |
| default_context(), test_server.GetURL("/set-cookie?CookieToNotSend=1"), |
| &d); |
| req->Start(); |
| d.RunUntilComplete(); |
| |
| EXPECT_EQ(0, default_network_delegate().blocked_annotate_cookies_count()); |
| EXPECT_EQ(0, default_network_delegate().blocked_set_cookie_count()); |
| } |
| |
| // Verify that the cookie is set. |
| { |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req = CreateFirstPartyRequest( |
| default_context(), test_server.GetURL("/echoheader?Cookie"), &d); |
| req->Start(); |
| d.RunUntilComplete(); |
| |
| EXPECT_TRUE(d.data_received().find("CookieToNotSend=1") != |
| std::string::npos); |
| |
| EXPECT_EQ(0, default_network_delegate().blocked_annotate_cookies_count()); |
| EXPECT_EQ(0, default_network_delegate().blocked_set_cookie_count()); |
| auto entries = net_log_observer_.GetEntries(); |
| for (const auto& entry : entries) { |
| EXPECT_NE(entry.type, |
| NetLogEventType::COOKIE_GET_BLOCKED_BY_NETWORK_DELEGATE); |
| } |
| } |
| |
| // Verify that the cookie isn't sent. |
| { |
| TestDelegate d; |
| default_network_delegate().set_cookie_options( |
| TestNetworkDelegate::NO_GET_COOKIES); |
| std::unique_ptr<URLRequest> req = CreateFirstPartyRequest( |
| default_context(), test_server.GetURL("/echoheader?Cookie"), &d); |
| req->Start(); |
| d.RunUntilComplete(); |
| |
| EXPECT_TRUE(d.data_received().find("Cookie: CookieToNotSend=1") == |
| std::string::npos); |
| |
| EXPECT_EQ(1, default_network_delegate().blocked_annotate_cookies_count()); |
| EXPECT_EQ(0, default_network_delegate().blocked_set_cookie_count()); |
| auto entries = net_log_observer_.GetEntries(); |
| ExpectLogContainsSomewhereAfter( |
| entries, 0, NetLogEventType::COOKIE_GET_BLOCKED_BY_NETWORK_DELEGATE, |
| NetLogEventPhase::NONE); |
| } |
| } |
| |
| // TODO(crbug.com/564656) This test is flaky on iOS. |
| #if BUILDFLAG(IS_IOS) |
| #define MAYBE_DoNotSaveCookies_ViaPolicy FLAKY_DoNotSaveCookies_ViaPolicy |
| #else |
| #define MAYBE_DoNotSaveCookies_ViaPolicy DoNotSaveCookies_ViaPolicy |
| #endif |
| TEST_F(URLRequestTest, MAYBE_DoNotSaveCookies_ViaPolicy) { |
| HttpTestServer test_server; |
| ASSERT_TRUE(test_server.Start()); |
| |
| // Set up a cookie. |
| { |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req = CreateFirstPartyRequest( |
| default_context(), |
| test_server.GetURL("/set-cookie?CookieToNotUpdate=2"), &d); |
| req->Start(); |
| d.RunUntilComplete(); |
| |
| EXPECT_EQ(0, default_network_delegate().blocked_annotate_cookies_count()); |
| EXPECT_EQ(0, default_network_delegate().blocked_set_cookie_count()); |
| auto entries = net_log_observer_.GetEntries(); |
| for (const auto& entry : entries) { |
| EXPECT_NE(entry.type, |
| NetLogEventType::COOKIE_SET_BLOCKED_BY_NETWORK_DELEGATE); |
| } |
| } |
| |
| // Try to set-up another cookie and update the previous cookie. |
| { |
| TestDelegate d; |
| default_network_delegate().set_cookie_options( |
| TestNetworkDelegate::NO_SET_COOKIE); |
| std::unique_ptr<URLRequest> req = CreateFirstPartyRequest( |
| default_context(), |
| test_server.GetURL("/set-cookie?CookieToNotSave=1&CookieToNotUpdate=1"), |
| &d); |
| req->Start(); |
| |
| d.RunUntilComplete(); |
| |
| EXPECT_EQ(0, default_network_delegate().blocked_annotate_cookies_count()); |
| EXPECT_EQ(2, default_network_delegate().blocked_set_cookie_count()); |
| auto entries = net_log_observer_.GetEntries(); |
| ExpectLogContainsSomewhereAfter( |
| entries, 0, NetLogEventType::COOKIE_SET_BLOCKED_BY_NETWORK_DELEGATE, |
| NetLogEventPhase::NONE); |
| } |
| |
| // Verify the cookies weren't saved or updated. |
| { |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req = CreateFirstPartyRequest( |
| default_context(), test_server.GetURL("/echoheader?Cookie"), &d); |
| req->Start(); |
| d.RunUntilComplete(); |
| |
| EXPECT_TRUE(d.data_received().find("CookieToNotSave=1") == |
| std::string::npos); |
| EXPECT_TRUE(d.data_received().find("CookieToNotUpdate=2") != |
| std::string::npos); |
| |
| EXPECT_EQ(0, default_network_delegate().blocked_annotate_cookies_count()); |
| EXPECT_EQ(2, default_network_delegate().blocked_set_cookie_count()); |
| } |
| } |
| |
| TEST_F(URLRequestTest, DoNotSaveEmptyCookies) { |
| HttpTestServer test_server; |
| ASSERT_TRUE(test_server.Start()); |
| |
| // Set up an empty cookie. |
| { |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req = CreateFirstPartyRequest( |
| default_context(), test_server.GetURL("/set-cookie"), &d); |
| req->Start(); |
| d.RunUntilComplete(); |
| |
| EXPECT_EQ(0, default_network_delegate().blocked_annotate_cookies_count()); |
| EXPECT_EQ(0, default_network_delegate().blocked_set_cookie_count()); |
| EXPECT_EQ(0, default_network_delegate().set_cookie_count()); |
| } |
| } |
| |
| TEST_F(URLRequestTest, DoNotSendCookies_ViaPolicy_Async) { |
| HttpTestServer test_server; |
| ASSERT_TRUE(test_server.Start()); |
| |
| // Set up a cookie. |
| { |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req = CreateFirstPartyRequest( |
| default_context(), test_server.GetURL("/set-cookie?CookieToNotSend=1"), |
| &d); |
| req->Start(); |
| d.RunUntilComplete(); |
| |
| EXPECT_EQ(0, default_network_delegate().blocked_annotate_cookies_count()); |
| EXPECT_EQ(0, default_network_delegate().blocked_set_cookie_count()); |
| } |
| |
| // Verify that the cookie is set. |
| { |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req = CreateFirstPartyRequest( |
| default_context(), test_server.GetURL("/echoheader?Cookie"), &d); |
| req->Start(); |
| d.RunUntilComplete(); |
| |
| EXPECT_TRUE(d.data_received().find("CookieToNotSend=1") != |
| std::string::npos); |
| |
| EXPECT_EQ(0, default_network_delegate().blocked_annotate_cookies_count()); |
| EXPECT_EQ(0, default_network_delegate().blocked_set_cookie_count()); |
| } |
| |
| // Verify that the cookie isn't sent. |
| { |
| TestDelegate d; |
| default_network_delegate().set_cookie_options( |
| TestNetworkDelegate::NO_GET_COOKIES); |
| std::unique_ptr<URLRequest> req = CreateFirstPartyRequest( |
| default_context(), test_server.GetURL("/echoheader?Cookie"), &d); |
| req->Start(); |
| d.RunUntilComplete(); |
| |
| EXPECT_TRUE(d.data_received().find("Cookie: CookieToNotSend=1") == |
| std::string::npos); |
| |
| EXPECT_EQ(1, default_network_delegate().blocked_annotate_cookies_count()); |
| EXPECT_EQ(0, default_network_delegate().blocked_set_cookie_count()); |
| } |
| } |
| |
| TEST_F(URLRequestTest, DoNotSaveCookies_ViaPolicy_Async) { |
| HttpTestServer test_server; |
| ASSERT_TRUE(test_server.Start()); |
| |
| // Set up a cookie. |
| { |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req = CreateFirstPartyRequest( |
| default_context(), |
| test_server.GetURL("/set-cookie?CookieToNotUpdate=2"), &d); |
| req->Start(); |
| d.RunUntilComplete(); |
| |
| EXPECT_EQ(0, default_network_delegate().blocked_annotate_cookies_count()); |
| EXPECT_EQ(0, default_network_delegate().blocked_set_cookie_count()); |
| } |
| |
| // Try to set-up another cookie and update the previous cookie. |
| { |
| TestDelegate d; |
| default_network_delegate().set_cookie_options( |
| TestNetworkDelegate::NO_SET_COOKIE); |
| std::unique_ptr<URLRequest> req = CreateFirstPartyRequest( |
| default_context(), |
| test_server.GetURL("/set-cookie?CookieToNotSave=1&CookieToNotUpdate=1"), |
| &d); |
| req->Start(); |
| |
| d.RunUntilComplete(); |
| |
| EXPECT_EQ(0, default_network_delegate().blocked_annotate_cookies_count()); |
| EXPECT_EQ(2, default_network_delegate().blocked_set_cookie_count()); |
| } |
| |
| // Verify the cookies weren't saved or updated. |
| { |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req = CreateFirstPartyRequest( |
| default_context(), test_server.GetURL("/echoheader?Cookie"), &d); |
| req->Start(); |
| d.RunUntilComplete(); |
| |
| EXPECT_TRUE(d.data_received().find("CookieToNotSave=1") == |
| std::string::npos); |
| EXPECT_TRUE(d.data_received().find("CookieToNotUpdate=2") != |
| std::string::npos); |
| |
| EXPECT_EQ(0, default_network_delegate().blocked_annotate_cookies_count()); |
| EXPECT_EQ(2, default_network_delegate().blocked_set_cookie_count()); |
| } |
| } |
| |
| // Tests for SameSite cookies. The test param indicates whether the same-site |
| // calculation considers redirect chains. |
| class URLRequestSameSiteCookiesTest |
| : public URLRequestTest, |
| public ::testing::WithParamInterface<bool> { |
| public: |
| URLRequestSameSiteCookiesTest() { |
| if (DoesCookieSameSiteConsiderRedirectChain()) { |
| feature_list_.InitAndEnableFeature( |
| features::kCookieSameSiteConsidersRedirectChain); |
| } |
| } |
| |
| bool DoesCookieSameSiteConsiderRedirectChain() { return GetParam(); } |
| |
| private: |
| base::test::ScopedFeatureList feature_list_; |
| }; |
| |
| TEST_P(URLRequestSameSiteCookiesTest, SameSiteCookies) { |
| HttpTestServer test_server; |
| ASSERT_TRUE(test_server.Start()); |
| |
| const std::string kHost = "example.test"; |
| const std::string kSubHost = "subdomain.example.test"; |
| const std::string kCrossHost = "cross-origin.test"; |
| const url::Origin kOrigin = |
| url::Origin::Create(test_server.GetURL(kHost, "/")); |
| const url::Origin kSubOrigin = |
| url::Origin::Create(test_server.GetURL(kSubHost, "/")); |
| const url::Origin kCrossOrigin = |
| url::Origin::Create(test_server.GetURL(kCrossHost, "/")); |
| const SiteForCookies kSiteForCookies = SiteForCookies::FromOrigin(kOrigin); |
| const SiteForCookies kCrossSiteForCookies = |
| SiteForCookies::FromOrigin(kCrossOrigin); |
| |
| // Set up two 'SameSite' cookies on 'example.test' |
| { |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req = CreateFirstPartyRequest( |
| default_context(), |
| test_server.GetURL(kHost, |
| "/set-cookie?StrictSameSiteCookie=1;SameSite=Strict&" |
| "LaxSameSiteCookie=1;SameSite=Lax"), |
| &d); |
| req->set_site_for_cookies(kSiteForCookies); |
| req->set_initiator(kOrigin); |
| req->Start(); |
| d.RunUntilComplete(); |
| EXPECT_EQ(0, default_network_delegate().blocked_annotate_cookies_count()); |
| EXPECT_EQ(0, default_network_delegate().blocked_set_cookie_count()); |
| EXPECT_EQ(2, default_network_delegate().set_cookie_count()); |
| } |
| |
| // Verify that both cookies are sent for same-site requests, whether they are |
| // subresource requests, subframe navigations, or main frame navigations. |
| for (IsolationInfo::RequestType request_type : |
| {IsolationInfo::RequestType::kMainFrame, |
| IsolationInfo::RequestType::kSubFrame, |
| IsolationInfo::RequestType::kOther}) { |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req(default_context().CreateRequest( |
| test_server.GetURL(kHost, "/echoheader?Cookie"), DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->set_isolation_info(IsolationInfo::Create(request_type, kOrigin, |
| kOrigin, kSiteForCookies, |
| {} /* party_context */)); |
| req->set_site_for_cookies(kSiteForCookies); |
| req->set_initiator(kOrigin); |
| req->Start(); |
| d.RunUntilComplete(); |
| |
| EXPECT_NE(std::string::npos, |
| d.data_received().find("StrictSameSiteCookie=1")); |
| EXPECT_NE(std::string::npos, d.data_received().find("LaxSameSiteCookie=1")); |
| EXPECT_EQ(0, default_network_delegate().blocked_annotate_cookies_count()); |
| EXPECT_EQ(0, default_network_delegate().blocked_set_cookie_count()); |
| } |
| |
| // Verify that both cookies are sent when the request has no initiator (can |
| // happen for main frame browser-initiated navigations). |
| { |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req(default_context().CreateRequest( |
| test_server.GetURL(kHost, "/echoheader?Cookie"), DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->set_site_for_cookies(kSiteForCookies); |
| req->Start(); |
| d.RunUntilComplete(); |
| |
| EXPECT_NE(std::string::npos, |
| d.data_received().find("StrictSameSiteCookie=1")); |
| EXPECT_NE(std::string::npos, d.data_received().find("LaxSameSiteCookie=1")); |
| EXPECT_EQ(0, default_network_delegate().blocked_annotate_cookies_count()); |
| EXPECT_EQ(0, default_network_delegate().blocked_set_cookie_count()); |
| } |
| |
| // Verify that both cookies are sent for same-registrable-domain requests. |
| { |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req(default_context().CreateRequest( |
| test_server.GetURL(kHost, "/echoheader?Cookie"), DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->set_site_for_cookies( |
| SiteForCookies::FromUrl(test_server.GetURL(kSubHost, "/"))); |
| req->set_initiator(kSubOrigin); |
| req->Start(); |
| d.RunUntilComplete(); |
| |
| EXPECT_NE(std::string::npos, |
| d.data_received().find("StrictSameSiteCookie=1")); |
| EXPECT_NE(std::string::npos, d.data_received().find("LaxSameSiteCookie=1")); |
| EXPECT_EQ(0, default_network_delegate().blocked_annotate_cookies_count()); |
| EXPECT_EQ(0, default_network_delegate().blocked_set_cookie_count()); |
| } |
| |
| // Verify that neither cookie is not sent for cross-site requests. |
| { |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req(default_context().CreateRequest( |
| test_server.GetURL(kHost, "/echoheader?Cookie"), DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->set_site_for_cookies(kCrossSiteForCookies); |
| req->set_initiator(kCrossOrigin); |
| req->Start(); |
| d.RunUntilComplete(); |
| |
| EXPECT_EQ(std::string::npos, |
| d.data_received().find("StrictSameSiteCookie=1")); |
| EXPECT_EQ(std::string::npos, d.data_received().find("LaxSameSiteCookie=1")); |
| EXPECT_EQ(0, default_network_delegate().blocked_annotate_cookies_count()); |
| EXPECT_EQ(0, default_network_delegate().blocked_set_cookie_count()); |
| } |
| |
| // Verify that the lax cookie is sent for cross-site initiators when the |
| // method is "safe" and the request is a main frame navigation. |
| { |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req(default_context().CreateRequest( |
| test_server.GetURL(kHost, "/echoheader?Cookie"), DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->set_isolation_info(IsolationInfo::Create( |
| IsolationInfo::RequestType::kMainFrame, kOrigin, kOrigin, |
| kSiteForCookies, {} /* party_context */)); |
| req->set_site_for_cookies(kSiteForCookies); |
| req->set_initiator(kCrossOrigin); |
| req->set_method("GET"); |
| req->Start(); |
| d.RunUntilComplete(); |
| |
| EXPECT_EQ(std::string::npos, |
| d.data_received().find("StrictSameSiteCookie=1")); |
| EXPECT_NE(std::string::npos, d.data_received().find("LaxSameSiteCookie=1")); |
| EXPECT_EQ(0, default_network_delegate().blocked_annotate_cookies_count()); |
| EXPECT_EQ(0, default_network_delegate().blocked_set_cookie_count()); |
| } |
| |
| // Verify that the lax cookie is sent for cross-site initiators when the |
| // method is "safe" and the request is being forced to be considered as a |
| // main frame navigation. |
| { |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req(default_context().CreateRequest( |
| test_server.GetURL(kHost, "/echoheader?Cookie"), DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->set_isolation_info(IsolationInfo::Create( |
| IsolationInfo::RequestType::kOther, kOrigin, kOrigin, kSiteForCookies, |
| {} /* party_context */)); |
| req->set_site_for_cookies(kSiteForCookies); |
| req->set_initiator(kCrossOrigin); |
| req->set_method("GET"); |
| req->set_force_main_frame_for_same_site_cookies(true); |
| req->Start(); |
| d.RunUntilComplete(); |
| |
| EXPECT_EQ(std::string::npos, |
| d.data_received().find("StrictSameSiteCookie=1")); |
| EXPECT_NE(std::string::npos, d.data_received().find("LaxSameSiteCookie=1")); |
| EXPECT_EQ(0, default_network_delegate().blocked_annotate_cookies_count()); |
| EXPECT_EQ(0, default_network_delegate().blocked_set_cookie_count()); |
| } |
| |
| // Verify that neither cookie is sent for cross-site initiators when the |
| // method is unsafe (e.g. POST), even if the request is a main frame |
| // navigation. |
| { |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req(default_context().CreateRequest( |
| test_server.GetURL(kHost, "/echoheader?Cookie"), DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->set_isolation_info(IsolationInfo::Create( |
| IsolationInfo::RequestType::kMainFrame, kOrigin, kOrigin, |
| kSiteForCookies, {} /* party_context */)); |
| req->set_site_for_cookies(kSiteForCookies); |
| req->set_initiator(kCrossOrigin); |
| req->set_method("POST"); |
| req->Start(); |
| d.RunUntilComplete(); |
| |
| EXPECT_EQ(std::string::npos, |
| d.data_received().find("StrictSameSiteCookie=1")); |
| EXPECT_EQ(std::string::npos, d.data_received().find("LaxSameSiteCookie=1")); |
| EXPECT_EQ(0, default_network_delegate().blocked_annotate_cookies_count()); |
| EXPECT_EQ(0, default_network_delegate().blocked_set_cookie_count()); |
| } |
| |
| // Verify that neither cookie is sent for cross-site initiators when the |
| // method is safe and the site-for-cookies is same-site, but the request is |
| // not a main frame navigation. |
| { |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req(default_context().CreateRequest( |
| test_server.GetURL(kHost, "/echoheader?Cookie"), DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->set_isolation_info(IsolationInfo::Create( |
| IsolationInfo::RequestType::kSubFrame, kOrigin, kOrigin, |
| kSiteForCookies, {} /* party_context */)); |
| req->set_site_for_cookies(kSiteForCookies); |
| req->set_initiator(kCrossOrigin); |
| req->set_method("GET"); |
| req->Start(); |
| d.RunUntilComplete(); |
| |
| EXPECT_EQ(std::string::npos, |
| d.data_received().find("StrictSameSiteCookie=1")); |
| EXPECT_EQ(std::string::npos, d.data_received().find("LaxSameSiteCookie=1")); |
| EXPECT_EQ(0, default_network_delegate().blocked_annotate_cookies_count()); |
| EXPECT_EQ(0, default_network_delegate().blocked_set_cookie_count()); |
| |
| // Check that the appropriate cookie inclusion status is set. |
| ASSERT_EQ(2u, req->maybe_sent_cookies().size()); |
| CookieInclusionStatus expected_strict_status = |
| CookieInclusionStatus::MakeFromReasonsForTesting( |
| {CookieInclusionStatus::EXCLUDE_SAMESITE_STRICT}, |
| {} /* warning_reasons */); |
| CookieInclusionStatus expected_lax_status = |
| CookieInclusionStatus::MakeFromReasonsForTesting( |
| {CookieInclusionStatus::EXCLUDE_SAMESITE_LAX}, |
| {} /* warning_reasons */); |
| EXPECT_EQ(expected_strict_status, |
| req->maybe_sent_cookies()[0].access_result.status); |
| EXPECT_EQ(expected_lax_status, |
| req->maybe_sent_cookies()[1].access_result.status); |
| } |
| } |
| |
| TEST_P(URLRequestSameSiteCookiesTest, SameSiteCookies_Redirect) { |
| EmbeddedTestServer http_server; |
| RegisterDefaultHandlers(&http_server); |
| EmbeddedTestServer https_server(EmbeddedTestServer::TYPE_HTTPS); |
| https_server.SetSSLConfig(EmbeddedTestServer::CERT_TEST_NAMES); |
| RegisterDefaultHandlers(&https_server); |
| ASSERT_TRUE(http_server.Start()); |
| ASSERT_TRUE(https_server.Start()); |
| |
| const std::string kHost = "foo.a.test"; |
| const std::string kSameSiteHost = "bar.a.test"; |
| const std::string kCrossSiteHost = "b.test"; |
| const url::Origin kOrigin = |
| url::Origin::Create(https_server.GetURL(kHost, "/")); |
| const url::Origin kHttpOrigin = |
| url::Origin::Create(http_server.GetURL(kHost, "/")); |
| const url::Origin kSameSiteOrigin = |
| url::Origin::Create(https_server.GetURL(kSameSiteHost, "/")); |
| const url::Origin kCrossSiteOrigin = |
| url::Origin::Create(https_server.GetURL(kCrossSiteHost, "/")); |
| const SiteForCookies kSiteForCookies = SiteForCookies::FromOrigin(kOrigin); |
| const SiteForCookies kHttpSiteForCookies = |
| SiteForCookies::FromOrigin(kHttpOrigin); |
| const SiteForCookies kCrossSiteForCookies = |
| SiteForCookies::FromOrigin(kCrossSiteOrigin); |
| |
| // Set up two 'SameSite' cookies on foo.a.test |
| { |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req = CreateFirstPartyRequest( |
| default_context(), |
| https_server.GetURL( |
| kHost, |
| "/set-cookie?StrictSameSiteCookie=1;SameSite=Strict&" |
| "LaxSameSiteCookie=1;SameSite=Lax"), |
| &d); |
| req->set_site_for_cookies(kSiteForCookies); |
| req->set_initiator(kOrigin); |
| req->Start(); |
| d.RunUntilComplete(); |
| ASSERT_EQ(2u, GetAllCookies(&default_context()).size()); |
| } |
| |
| // Verify that both cookies are sent for same-site, unredirected requests. |
| { |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req(default_context().CreateRequest( |
| https_server.GetURL(kHost, "/echoheader?Cookie"), DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->set_site_for_cookies(kSiteForCookies); |
| req->set_initiator(kOrigin); |
| req->Start(); |
| d.RunUntilComplete(); |
| |
| EXPECT_EQ(1u, req->url_chain().size()); |
| EXPECT_NE(std::string::npos, |
| d.data_received().find("StrictSameSiteCookie=1")); |
| EXPECT_NE(std::string::npos, d.data_received().find("LaxSameSiteCookie=1")); |
| } |
| |
| // Verify that both cookies are sent for a same-origin redirected top level |
| // navigation. |
| { |
| TestDelegate d; |
| GURL url = https_server.GetURL( |
| kHost, "/server-redirect?" + |
| https_server.GetURL(kHost, "/echoheader?Cookie").spec()); |
| std::unique_ptr<URLRequest> req(default_context().CreateRequest( |
| url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->set_isolation_info(IsolationInfo::Create( |
| IsolationInfo::RequestType::kMainFrame, kOrigin, kOrigin, |
| kSiteForCookies, {} /* party_context */)); |
| req->set_first_party_url_policy( |
| RedirectInfo::FirstPartyURLPolicy::UPDATE_URL_ON_REDIRECT); |
| req->set_site_for_cookies(kSiteForCookies); |
| req->set_initiator(kOrigin); |
| req->Start(); |
| d.RunUntilComplete(); |
| |
| EXPECT_EQ(2u, req->url_chain().size()); |
| EXPECT_NE(std::string::npos, |
| d.data_received().find("StrictSameSiteCookie=1")); |
| EXPECT_NE(std::string::npos, d.data_received().find("LaxSameSiteCookie=1")); |
| } |
| |
| // Verify that both cookies are sent for a same-site redirected top level |
| // navigation. |
| { |
| TestDelegate d; |
| GURL url = https_server.GetURL( |
| kSameSiteHost, |
| "/server-redirect?" + |
| https_server.GetURL(kHost, "/echoheader?Cookie").spec()); |
| std::unique_ptr<URLRequest> req(default_context().CreateRequest( |
| url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->set_isolation_info(IsolationInfo::Create( |
| IsolationInfo::RequestType::kMainFrame, kSameSiteOrigin, |
| kSameSiteOrigin, kSiteForCookies, {} /* party_context */)); |
| req->set_first_party_url_policy( |
| RedirectInfo::FirstPartyURLPolicy::UPDATE_URL_ON_REDIRECT); |
| req->set_site_for_cookies(kSiteForCookies); |
| req->set_initiator(kOrigin); |
| req->Start(); |
| d.RunUntilComplete(); |
| |
| EXPECT_EQ(2u, req->url_chain().size()); |
| EXPECT_NE(std::string::npos, |
| d.data_received().find("StrictSameSiteCookie=1")); |
| EXPECT_NE(std::string::npos, d.data_received().find("LaxSameSiteCookie=1")); |
| } |
| |
| // If redirect chains are considered: |
| // Verify that the Strict cookie may or may not be sent for a cross-scheme |
| // (same-registrable-domain) redirected top level navigation, depending on the |
| // status of Schemeful Same-Site. The Lax cookie is sent regardless, because |
| // this is a top-level navigation. |
| // |
| // If redirect chains are not considered: |
| // Verify that both cookies are sent, because this is a top-level navigation. |
| { |
| base::test::ScopedFeatureList feature_list; |
| feature_list.InitAndDisableFeature(features::kSchemefulSameSite); |
| TestDelegate d; |
| GURL url = http_server.GetURL( |
| kHost, "/server-redirect?" + |
| https_server.GetURL(kHost, "/echoheader?Cookie").spec()); |
| std::unique_ptr<URLRequest> req(default_context().CreateRequest( |
| url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->set_isolation_info(IsolationInfo::Create( |
| IsolationInfo::RequestType::kMainFrame, kHttpOrigin, kHttpOrigin, |
| kHttpSiteForCookies, {} /* party_context */)); |
| req->set_first_party_url_policy( |
| RedirectInfo::FirstPartyURLPolicy::UPDATE_URL_ON_REDIRECT); |
| req->set_site_for_cookies(kHttpSiteForCookies); |
| req->set_initiator(kOrigin); |
| req->Start(); |
| d.RunUntilComplete(); |
| |
| EXPECT_EQ(2u, req->url_chain().size()); |
| EXPECT_NE(std::string::npos, |
| d.data_received().find("StrictSameSiteCookie=1")); |
| EXPECT_NE(std::string::npos, d.data_received().find("LaxSameSiteCookie=1")); |
| } |
| { |
| base::test::ScopedFeatureList feature_list; |
| feature_list.InitAndEnableFeature(features::kSchemefulSameSite); |
| TestDelegate d; |
| GURL url = http_server.GetURL( |
| kHost, "/server-redirect?" + |
| https_server.GetURL(kHost, "/echoheader?Cookie").spec()); |
| std::unique_ptr<URLRequest> req(default_context().CreateRequest( |
| url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->set_isolation_info(IsolationInfo::Create( |
| IsolationInfo::RequestType::kMainFrame, kHttpOrigin, kHttpOrigin, |
| kHttpSiteForCookies, {} /* party_context */)); |
| req->set_first_party_url_policy( |
| RedirectInfo::FirstPartyURLPolicy::UPDATE_URL_ON_REDIRECT); |
| req->set_site_for_cookies(kHttpSiteForCookies); |
| req->set_initiator(kOrigin); |
| req->Start(); |
| d.RunUntilComplete(); |
| |
| EXPECT_EQ(2u, req->url_chain().size()); |
| EXPECT_EQ( |
| DoesCookieSameSiteConsiderRedirectChain(), |
| std::string::npos == d.data_received().find("StrictSameSiteCookie=1")); |
| EXPECT_NE(std::string::npos, d.data_received().find("LaxSameSiteCookie=1")); |
| } |
| |
| // Verify that (depending on whether redirect chains are considered), the |
| // Strict cookie is (not) sent for a cross-site redirected top level |
| // navigation... |
| { |
| TestDelegate d; |
| GURL url = https_server.GetURL( |
| kCrossSiteHost, |
| "/server-redirect?" + |
| https_server.GetURL(kHost, "/echoheader?Cookie").spec()); |
| std::unique_ptr<URLRequest> req(default_context().CreateRequest( |
| url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->set_isolation_info(IsolationInfo::Create( |
| IsolationInfo::RequestType::kMainFrame, kCrossSiteOrigin, |
| kCrossSiteOrigin, kCrossSiteForCookies, {} /* party_context */)); |
| req->set_first_party_url_policy( |
| RedirectInfo::FirstPartyURLPolicy::UPDATE_URL_ON_REDIRECT); |
| req->set_site_for_cookies(kCrossSiteForCookies); |
| req->set_initiator(kOrigin); |
| req->Start(); |
| d.RunUntilComplete(); |
| |
| EXPECT_EQ(2u, req->url_chain().size()); |
| EXPECT_EQ( |
| DoesCookieSameSiteConsiderRedirectChain(), |
| std::string::npos == d.data_received().find("StrictSameSiteCookie=1")); |
| EXPECT_NE(std::string::npos, d.data_received().find("LaxSameSiteCookie=1")); |
| } |
| // ... even if the initial URL is same-site. |
| { |
| TestDelegate d; |
| GURL middle_url = https_server.GetURL( |
| kCrossSiteHost, |
| "/server-redirect?" + |
| https_server.GetURL(kHost, "/echoheader?Cookie").spec()); |
| GURL url = |
| https_server.GetURL(kHost, "/server-redirect?" + middle_url.spec()); |
| std::unique_ptr<URLRequest> req(default_context().CreateRequest( |
| url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->set_isolation_info(IsolationInfo::Create( |
| IsolationInfo::RequestType::kMainFrame, kOrigin, kOrigin, |
| kSiteForCookies, {} /* party_context */)); |
| req->set_first_party_url_policy( |
| RedirectInfo::FirstPartyURLPolicy::UPDATE_URL_ON_REDIRECT); |
| req->set_site_for_cookies(kSiteForCookies); |
| req->set_initiator(kOrigin); |
| req->Start(); |
| d.RunUntilComplete(); |
| |
| EXPECT_EQ(3u, req->url_chain().size()); |
| EXPECT_EQ( |
| DoesCookieSameSiteConsiderRedirectChain(), |
| std::string::npos == d.data_received().find("StrictSameSiteCookie=1")); |
| EXPECT_NE(std::string::npos, d.data_received().find("LaxSameSiteCookie=1")); |
| } |
| |
| // Verify that (depending on whether redirect chains are considered), neither |
| // (or both) SameSite cookie is sent for a cross-site redirected subresource |
| // request... |
| { |
| TestDelegate d; |
| GURL url = https_server.GetURL( |
| kCrossSiteHost, |
| "/server-redirect?" + |
| https_server.GetURL(kHost, "/echoheader?Cookie").spec()); |
| std::unique_ptr<URLRequest> req(default_context().CreateRequest( |
| url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->set_isolation_info(IsolationInfo::Create( |
| IsolationInfo::RequestType::kOther, kOrigin, kOrigin, kSiteForCookies, |
| {} /* party_context */)); |
| req->set_site_for_cookies(kSiteForCookies); |
| req->set_initiator(kOrigin); |
| req->Start(); |
| d.RunUntilComplete(); |
| |
| EXPECT_EQ(2u, req->url_chain().size()); |
| EXPECT_EQ( |
| DoesCookieSameSiteConsiderRedirectChain(), |
| std::string::npos == d.data_received().find("StrictSameSiteCookie=1")); |
| EXPECT_EQ( |
| DoesCookieSameSiteConsiderRedirectChain(), |
| std::string::npos == d.data_received().find("LaxSameSiteCookie=1")); |
| } |
| // ... even if the initial URL is same-site. |
| { |
| TestDelegate d; |
| GURL middle_url = https_server.GetURL( |
| kCrossSiteHost, |
| "/server-redirect?" + |
| https_server.GetURL(kHost, "/echoheader?Cookie").spec()); |
| GURL url = |
| https_server.GetURL(kHost, "/server-redirect?" + middle_url.spec()); |
| std::unique_ptr<URLRequest> req(default_context().CreateRequest( |
| url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->set_isolation_info(IsolationInfo::Create( |
| IsolationInfo::RequestType::kOther, kOrigin, kOrigin, kSiteForCookies, |
| {} /* party_context */)); |
| req->set_site_for_cookies(kSiteForCookies); |
| req->set_initiator(kOrigin); |
| req->Start(); |
| d.RunUntilComplete(); |
| |
| EXPECT_EQ(3u, req->url_chain().size()); |
| EXPECT_EQ( |
| DoesCookieSameSiteConsiderRedirectChain(), |
| std::string::npos == d.data_received().find("StrictSameSiteCookie=1")); |
| EXPECT_EQ( |
| DoesCookieSameSiteConsiderRedirectChain(), |
| std::string::npos == d.data_received().find("LaxSameSiteCookie=1")); |
| } |
| } |
| |
| TEST_P(URLRequestSameSiteCookiesTest, SettingSameSiteCookies) { |
| HttpTestServer test_server; |
| ASSERT_TRUE(test_server.Start()); |
| |
| const std::string kHost = "example.test"; |
| const std::string kSubHost = "subdomain.example.test"; |
| const std::string kCrossHost = "cross-origin.test"; |
| const url::Origin kOrigin = |
| url::Origin::Create(test_server.GetURL(kHost, "/")); |
| const url::Origin kSubOrigin = |
| url::Origin::Create(test_server.GetURL(kSubHost, "/")); |
| const url::Origin kCrossOrigin = |
| url::Origin::Create(test_server.GetURL(kCrossHost, "/")); |
| const SiteForCookies kSiteForCookies = SiteForCookies::FromOrigin(kOrigin); |
| const SiteForCookies kCrossSiteForCookies = |
| SiteForCookies::FromOrigin(kCrossOrigin); |
| |
| int expected_cookies = 0; |
| |
| { |
| TestDelegate d; |
| std::unique_ptr<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(kSiteForCookies); |
| req->set_initiator(kOrigin); |
| |
| // 'SameSite' cookies are settable from strict same-site contexts |
| // (same-origin site_for_cookies, same-origin initiator), so this request |
| // should result in two cookies being set. |
| expected_cookies += 2; |
| |
| req->Start(); |
| d.RunUntilComplete(); |
| EXPECT_EQ(expected_cookies, |
| static_cast<int>(GetAllCookies(&default_context()).size())); |
| EXPECT_EQ(expected_cookies, default_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_isolation_info(IsolationInfo::Create( |
| IsolationInfo::RequestType::kMainFrame, kOrigin, kOrigin, |
| kSiteForCookies, {} /* party_context */)); |
| req->set_site_for_cookies(kSiteForCookies); |
| req->set_initiator(kCrossOrigin); |
| |
| // 'SameSite' cookies are settable from lax same-site contexts (same-origin |
| // site_for_cookies, cross-site initiator, main frame navigation), so this |
| // request should result in two cookies being set. |
| expected_cookies += 2; |
| |
| req->Start(); |
| d.RunUntilComplete(); |
| EXPECT_EQ(expected_cookies, |
| static_cast<int>(GetAllCookies(&default_context()).size())); |
| EXPECT_EQ(expected_cookies, default_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_isolation_info(IsolationInfo::Create( |
| IsolationInfo::RequestType::kMainFrame, kSubOrigin, kSubOrigin, |
| kSiteForCookies, {} /* party_context */)); |
| req->set_site_for_cookies( |
| SiteForCookies::FromUrl(test_server.GetURL(kSubHost, "/"))); |
| req->set_initiator(kCrossOrigin); |
| |
| // 'SameSite' cookies are settable from lax same-site contexts (same-site |
| // site_for_cookies, cross-site initiator, main frame navigation), so this |
| // request should result in two cookies being set. |
| expected_cookies += 2; |
| |
| req->Start(); |
| d.RunUntilComplete(); |
| EXPECT_EQ(expected_cookies, |
| static_cast<int>(GetAllCookies(&default_context()).size())); |
| EXPECT_EQ(expected_cookies, default_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, default_network_delegate().set_cookie_count()); |
| } |
| |
| int expected_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(kCrossSiteForCookies); |
| req->set_initiator(kCrossOrigin); |
| |
| // 'SameSite' cookies are not settable from cross-site contexts, so this |
| // should not result in any new cookies being set. |
| expected_cookies += 0; |
| // This counts the number of successful calls to CanSetCookie() when |
| // attempting to set a cookie. The two cookies above were created and |
| // attempted to be set, and were not rejected by the NetworkDelegate, so the |
| // count here is 2 more than the number of cookies actually set. |
| expected_network_delegate_set_cookie_count = expected_cookies + 2; |
| |
| req->Start(); |
| d.RunUntilComplete(); |
| // This counts the number of cookies actually set. |
| EXPECT_EQ(expected_cookies, |
| static_cast<int>(GetAllCookies(&default_context()).size())); |
| EXPECT_EQ(expected_network_delegate_set_cookie_count, |
| default_network_delegate().set_cookie_count()); |
| } |
| |
| { |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req(default_context().CreateRequest( |
| test_server.GetURL(kHost, |
| "/set-cookie?Strict6=1;SameSite=Strict&" |
| "Lax6=1;SameSite=Lax"), |
| DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->set_isolation_info(IsolationInfo::Create( |
| IsolationInfo::RequestType::kSubFrame, kOrigin, kOrigin, |
| kSiteForCookies, {} /* party_context */)); |
| req->set_site_for_cookies(kSiteForCookies); |
| req->set_initiator(kCrossOrigin); |
| |
| // Same-site site-for-cookies, cross-site initiator, non main frame |
| // navigation -> context is considered cross-site so no SameSite cookies are |
| // set. |
| expected_cookies += 0; |
| // This counts the number of successful calls to CanSetCookie() when |
| // attempting to set a cookie. The two cookies above were created and |
| // attempted to be set, and were not rejected by the NetworkDelegate, so the |
| // count here is 2 more than the number of cookies actually set. |
| expected_network_delegate_set_cookie_count += 2; |
| |
| req->Start(); |
| d.RunUntilComplete(); |
| EXPECT_EQ(expected_cookies, |
| static_cast<int>(GetAllCookies(&default_context()).size())); |
| EXPECT_EQ(expected_network_delegate_set_cookie_count, |
| default_network_delegate().set_cookie_count()); |
| |
| // Check that the appropriate cookie inclusion status is set. |
| ASSERT_EQ(2u, req->maybe_stored_cookies().size()); |
| CookieInclusionStatus expected_strict_status = |
| CookieInclusionStatus::MakeFromReasonsForTesting( |
| {CookieInclusionStatus::EXCLUDE_SAMESITE_STRICT}, |
| {} /* warning_reasons */); |
| CookieInclusionStatus expected_lax_status = |
| CookieInclusionStatus::MakeFromReasonsForTesting( |
| {CookieInclusionStatus::EXCLUDE_SAMESITE_LAX}, |
| {} /* warning_reasons */); |
| EXPECT_EQ(expected_strict_status, |
| req->maybe_stored_cookies()[0].access_result.status); |
| EXPECT_EQ(expected_lax_status, |
| req->maybe_stored_cookies()[1].access_result.status); |
| } |
| |
| { |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req(default_context().CreateRequest( |
| test_server.GetURL(kHost, |
| "/set-cookie?Strict7=1;SameSite=Strict&" |
| "Lax7=1;SameSite=Lax"), |
| DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->set_isolation_info(IsolationInfo::Create( |
| IsolationInfo::RequestType::kOther, kOrigin, kOrigin, kSiteForCookies, |
| {} /* party_context */)); |
| req->set_site_for_cookies(kSiteForCookies); |
| req->set_initiator(kCrossOrigin); |
| req->set_force_main_frame_for_same_site_cookies(true); |
| |
| // 'SameSite' cookies are settable from lax same-site contexts (same-origin |
| // site_for_cookies, cross-site initiator, main frame navigation), so this |
| // request should result in two cookies being set. |
| expected_cookies += 2; |
| expected_network_delegate_set_cookie_count += 2; |
| |
| req->Start(); |
| d.RunUntilComplete(); |
| EXPECT_EQ(expected_cookies, |
| static_cast<int>(GetAllCookies(&default_context()).size())); |
| EXPECT_EQ(expected_network_delegate_set_cookie_count, |
| default_network_delegate().set_cookie_count()); |
| } |
| } |
| |
| // Tests special chrome:// scheme that is supposed to always attach SameSite |
| // cookies if the requested site is secure. |
| TEST_P(URLRequestSameSiteCookiesTest, SameSiteCookiesSpecialScheme) { |
| url::ScopedSchemeRegistryForTests scoped_registry; |
| url::AddStandardScheme("chrome", url::SchemeType::SCHEME_WITH_HOST); |
| |
| EmbeddedTestServer https_test_server(EmbeddedTestServer::TYPE_HTTPS); |
| RegisterDefaultHandlers(&https_test_server); |
| ASSERT_TRUE(https_test_server.Start()); |
| EmbeddedTestServer http_test_server(EmbeddedTestServer::TYPE_HTTP); |
| RegisterDefaultHandlers(&http_test_server); |
| ASSERT_TRUE(http_test_server.Start()); |
| ASSERT_NE(https_test_server.port(), http_test_server.port()); |
| // Both hostnames should be 127.0.0.1 (so that we can use the same set of |
| // cookies on both, for convenience). |
| ASSERT_EQ(https_test_server.host_port_pair().host(), |
| http_test_server.host_port_pair().host()); |
| |
| // Set up special schemes |
| auto cad = std::make_unique<TestCookieAccessDelegate>(); |
| cad->SetIgnoreSameSiteRestrictionsScheme("chrome", true); |
| auto cm = std::make_unique<CookieMonster>(nullptr, nullptr); |
| cm->SetCookieAccessDelegate(std::move(cad)); |
| |
| auto context_builder = CreateTestURLRequestContextBuilder(); |
| context_builder->SetCookieStore(std::move(cm)); |
| auto context = context_builder->Build(); |
| |
| // SameSite cookies are not set for 'chrome' scheme if requested origin is not |
| // secure. |
| { |
| TestDelegate d; |
| std::unique_ptr<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.get()).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.get()); |
| EXPECT_EQ(2u, cookies.size()); |
| } |
| |
| // Verify that they are both sent when the site_for_cookies scheme is |
| // 'chrome' and the requested origin is secure. |
| { |
| TestDelegate d; |
| std::unique_ptr<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")); |
| } |
| } |
| |
| TEST_P(URLRequestSameSiteCookiesTest, SettingSameSiteCookies_Redirect) { |
| EmbeddedTestServer http_server; |
| RegisterDefaultHandlers(&http_server); |
| EmbeddedTestServer https_server(EmbeddedTestServer::TYPE_HTTPS); |
| https_server.SetSSLConfig(EmbeddedTestServer::CERT_TEST_NAMES); |
| RegisterDefaultHandlers(&https_server); |
| ASSERT_TRUE(http_server.Start()); |
| ASSERT_TRUE(https_server.Start()); |
| |
| auto& network_delegate = default_network_delegate(); |
| |
| const std::string kHost = "foo.a.test"; |
| const std::string kSameSiteHost = "bar.a.test"; |
| const std::string kCrossSiteHost = "b.test"; |
| const url::Origin kOrigin = |
| url::Origin::Create(https_server.GetURL(kHost, "/")); |
| const url::Origin kHttpOrigin = |
| url::Origin::Create(http_server.GetURL(kHost, "/")); |
| const url::Origin kSameSiteOrigin = |
| url::Origin::Create(https_server.GetURL(kSameSiteHost, "/")); |
| const url::Origin kCrossSiteOrigin = |
| url::Origin::Create(https_server.GetURL(kCrossSiteHost, "/")); |
| const SiteForCookies kSiteForCookies = SiteForCookies::FromOrigin(kOrigin); |
| const SiteForCookies kHttpSiteForCookies = |
| SiteForCookies::FromOrigin(kHttpOrigin); |
| const SiteForCookies kCrossSiteForCookies = |
| SiteForCookies::FromOrigin(kCrossSiteOrigin); |
| |
| int expected_cookies = 0; |
| int expected_set_cookie_count = 0; |
| |
| // Verify that SameSite cookies can be set for a same-origin redirected |
| // top-level navigation request. |
| { |
| TestDelegate d; |
| GURL set_cookie_url = https_server.GetURL( |
| kHost, "/set-cookie?Strict1=1;SameSite=Strict&Lax1=1;SameSite=Lax"); |
| GURL url = |
| https_server.GetURL(kHost, "/server-redirect?" + set_cookie_url.spec()); |
| std::unique_ptr<URLRequest> req(default_context().CreateRequest( |
| url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->set_isolation_info(IsolationInfo::Create( |
| IsolationInfo::RequestType::kMainFrame, kOrigin, kOrigin, |
| kSiteForCookies, {} /* party_context */)); |
| req->set_first_party_url_policy( |
| RedirectInfo::FirstPartyURLPolicy::UPDATE_URL_ON_REDIRECT); |
| req->set_site_for_cookies(kSiteForCookies); |
| req->set_initiator(kOrigin); |
| |
| expected_cookies += 2; |
| expected_set_cookie_count += 2; |
| |
| req->Start(); |
| d.RunUntilComplete(); |
| EXPECT_EQ(expected_cookies, |
| static_cast<int>(GetAllCookies(&default_context()).size())); |
| EXPECT_EQ(expected_set_cookie_count, network_delegate.set_cookie_count()); |
| } |
| |
| // Verify that SameSite cookies can be set for a same-site redirected |
| // top-level navigation request. |
| { |
| TestDelegate d; |
| GURL set_cookie_url = https_server.GetURL( |
| kHost, "/set-cookie?Strict2=1;SameSite=Strict&Lax2=1;SameSite=Lax"); |
| GURL url = https_server.GetURL(kSameSiteHost, |
| "/server-redirect?" + set_cookie_url.spec()); |
| std::unique_ptr<URLRequest> req(default_context().CreateRequest( |
| url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->set_isolation_info(IsolationInfo::Create( |
| IsolationInfo::RequestType::kMainFrame, kSameSiteOrigin, |
| kSameSiteOrigin, kSiteForCookies, {} /* party_context */)); |
| req->set_first_party_url_policy( |
| RedirectInfo::FirstPartyURLPolicy::UPDATE_URL_ON_REDIRECT); |
| req->set_site_for_cookies(kSiteForCookies); |
| req->set_initiator(kSameSiteOrigin); |
| |
| expected_cookies += 2; |
| expected_set_cookie_count += 2; |
| |
| req->Start(); |
| d.RunUntilComplete(); |
| EXPECT_EQ(expected_cookies, |
| static_cast<int>(GetAllCookies(&default_context()).size())); |
| EXPECT_EQ(expected_set_cookie_count, network_delegate.set_cookie_count()); |
| } |
| |
| // Verify that SameSite cookies can be set for a cross-site redirected |
| // top-level navigation request. |
| { |
| TestDelegate d; |
| GURL set_cookie_url = https_server.GetURL( |
| kHost, "/set-cookie?Strict3=1;SameSite=Strict&Lax3=1;SameSite=Lax"); |
| GURL url = https_server.GetURL(kCrossSiteHost, |
| "/server-redirect?" + set_cookie_url.spec()); |
| std::unique_ptr<URLRequest> req(default_context().CreateRequest( |
| url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->set_isolation_info(IsolationInfo::Create( |
| IsolationInfo::RequestType::kMainFrame, kCrossSiteOrigin, |
| kCrossSiteOrigin, kCrossSiteForCookies, {} /* party_context */)); |
| req->set_first_party_url_policy( |
| RedirectInfo::FirstPartyURLPolicy::UPDATE_URL_ON_REDIRECT); |
| req->set_site_for_cookies(kCrossSiteForCookies); |
| req->set_initiator(kCrossSiteOrigin); |
| |
| expected_cookies += 2; |
| expected_set_cookie_count += 2; |
| |
| req->Start(); |
| d.RunUntilComplete(); |
| EXPECT_EQ(expected_cookies, |
| static_cast<int>(GetAllCookies(&default_context()).size())); |
| EXPECT_EQ(expected_set_cookie_count, network_delegate.set_cookie_count()); |
| } |
| |
| // Verify that SameSite cookies can be set for a same-origin redirected |
| // subresource request. |
| { |
| TestDelegate d; |
| GURL set_cookie_url = https_server.GetURL( |
| kHost, "/set-cookie?Strict4=1;SameSite=Strict&Lax4=1;SameSite=Lax"); |
| GURL url = |
| https_server.GetURL(kHost, "/server-redirect?" + set_cookie_url.spec()); |
| std::unique_ptr<URLRequest> req(default_context().CreateRequest( |
| url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->set_isolation_info(IsolationInfo::Create( |
| IsolationInfo::RequestType::kOther, kOrigin, kOrigin, kSiteForCookies, |
| {} /* party_context */)); |
| req->set_site_for_cookies(kSiteForCookies); |
| req->set_initiator(kOrigin); |
| |
| expected_cookies += 2; |
| expected_set_cookie_count += 2; |
| |
| req->Start(); |
| d.RunUntilComplete(); |
| EXPECT_EQ(expected_cookies, |
| static_cast<int>(GetAllCookies(&default_context()).size())); |
| EXPECT_EQ(expected_set_cookie_count, network_delegate.set_cookie_count()); |
| } |
| |
| // Verify that SameSite cookies can be set for a same-site redirected |
| // subresource request. |
| { |
| TestDelegate d; |
| GURL set_cookie_url = https_server.GetURL( |
| kHost, "/set-cookie?Strict5=1;SameSite=Strict&Lax5=1;SameSite=Lax"); |
| GURL url = https_server.GetURL(kSameSiteHost, |
| "/server-redirect?" + set_cookie_url.spec()); |
| std::unique_ptr<URLRequest> req(default_context().CreateRequest( |
| url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->set_isolation_info(IsolationInfo::Create( |
| IsolationInfo::RequestType::kOther, kSameSiteOrigin, kSameSiteOrigin, |
| kSiteForCookies, {} /* party_context */)); |
| req->set_site_for_cookies(kSiteForCookies); |
| req->set_initiator(kSameSiteOrigin); |
| |
| expected_cookies += 2; |
| expected_set_cookie_count += 2; |
| |
| req->Start(); |
| d.RunUntilComplete(); |
| EXPECT_EQ(expected_cookies, |
| static_cast<int>(GetAllCookies(&default_context()).size())); |
| EXPECT_EQ(expected_set_cookie_count, network_delegate.set_cookie_count()); |
| } |
| |
| // Verify that (depending on whether redirect chains are considered) SameSite |
| // cookies can/cannot be set for a cross-site redirected subresource request, |
| // even if the site-for-cookies and initiator are same-site, ... |
| { |
| TestDelegate d; |
| GURL set_cookie_url = https_server.GetURL( |
| kHost, "/set-cookie?Strict6=1;SameSite=Strict&Lax6=1;SameSite=Lax"); |
| GURL url = https_server.GetURL(kCrossSiteHost, |
| "/server-redirect?" + set_cookie_url.spec()); |
| std::unique_ptr<URLRequest> req(default_context().CreateRequest( |
| url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->set_isolation_info(IsolationInfo::Create( |
| IsolationInfo::RequestType::kOther, kOrigin, kOrigin, kSiteForCookies, |
| {} /* party_context */)); |
| req->set_site_for_cookies(kSiteForCookies); |
| req->set_initiator(kOrigin); |
| |
| expected_cookies += DoesCookieSameSiteConsiderRedirectChain() ? 0 : 2; |
| expected_set_cookie_count += 2; |
| |
| req->Start(); |
| d.RunUntilComplete(); |
| EXPECT_EQ(expected_cookies, |
| static_cast<int>(GetAllCookies(&default_context()).size())); |
| EXPECT_EQ(expected_set_cookie_count, network_delegate.set_cookie_count()); |
| } |
| // ... even if the initial URL is same-site. |
| { |
| TestDelegate d; |
| GURL set_cookie_url = https_server.GetURL( |
| kHost, "/set-cookie?Strict7=1;SameSite=Strict&Lax7=1;SameSite=Lax"); |
| GURL middle_url = https_server.GetURL( |
| kCrossSiteHost, "/server-redirect?" + set_cookie_url.spec()); |
| GURL url = |
| https_server.GetURL(kHost, "/server-redirect?" + middle_url.spec()); |
| std::unique_ptr<URLRequest> req(default_context().CreateRequest( |
| url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->set_isolation_info(IsolationInfo::Create( |
| IsolationInfo::RequestType::kOther, kOrigin, kOrigin, kSiteForCookies, |
| {} /* party_context */)); |
| req->set_site_for_cookies(kSiteForCookies); |
| req->set_initiator(kOrigin); |
| |
| expected_cookies += DoesCookieSameSiteConsiderRedirectChain() ? 0 : 2; |
| expected_set_cookie_count += 2; |
| |
| req->Start(); |
| d.RunUntilComplete(); |
| EXPECT_EQ(expected_cookies, |
| static_cast<int>(GetAllCookies(&default_context()).size())); |
| EXPECT_EQ(expected_set_cookie_count, network_delegate.set_cookie_count()); |
| } |
| |
| // Verify that SameSite cookies may or may not be set for a cross-scheme |
| // (same-registrable-domain) redirected subresource request, depending on the |
| // status of Schemeful Same-Site and whether redirect chains are considered. |
| { |
| base::test::ScopedFeatureList feature_list; |
| feature_list.InitAndDisableFeature(features::kSchemefulSameSite); |
| TestDelegate d; |
| GURL set_cookie_url = https_server.GetURL( |
| kHost, "/set-cookie?Strict8=1;SameSite=Strict&Lax8=1;SameSite=Lax"); |
| GURL url = |
| http_server.GetURL(kHost, "/server-redirect?" + set_cookie_url.spec()); |
| std::unique_ptr<URLRequest> req(default_context().CreateRequest( |
| url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->set_isolation_info(IsolationInfo::Create( |
| IsolationInfo::RequestType::kOther, kHttpOrigin, kHttpOrigin, |
| kHttpSiteForCookies, {} /* party_context */)); |
| req->set_first_party_url_policy( |
| RedirectInfo::FirstPartyURLPolicy::UPDATE_URL_ON_REDIRECT); |
| req->set_site_for_cookies(kHttpSiteForCookies); |
| req->set_initiator(kOrigin); |
| |
| expected_cookies += 2; |
| expected_set_cookie_count += 2; |
| |
| req->Start(); |
| d.RunUntilComplete(); |
| EXPECT_EQ(expected_cookies, |
| static_cast<int>(GetAllCookies(&default_context()).size())); |
| EXPECT_EQ(expected_set_cookie_count, network_delegate.set_cookie_count()); |
| } |
| { |
| base::test::ScopedFeatureList feature_list; |
| feature_list.InitAndEnableFeature(features::kSchemefulSameSite); |
| TestDelegate d; |
| GURL set_cookie_url = https_server.GetURL( |
| kHost, "/set-cookie?Strict9=1;SameSite=Strict&Lax9=1;SameSite=Lax"); |
| GURL url = |
| http_server.GetURL(kHost, "/server-redirect?" + set_cookie_url.spec()); |
| std::unique_ptr<URLRequest> req(default_context().CreateRequest( |
| url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->set_isolation_info(IsolationInfo::Create( |
| IsolationInfo::RequestType::kOther, kHttpOrigin, kHttpOrigin, |
| kHttpSiteForCookies, {} /* party_context */)); |
| req->set_first_party_url_policy( |
| RedirectInfo::FirstPartyURLPolicy::UPDATE_URL_ON_REDIRECT); |
| req->set_site_for_cookies(kHttpSiteForCookies); |
| req->set_initiator(kOrigin); |
| |
| expected_cookies += DoesCookieSameSiteConsiderRedirectChain() ? 0 : 2; |
| expected_set_cookie_count += 2; |
| |
| req->Start(); |
| d.RunUntilComplete(); |
| EXPECT_EQ(expected_cookies, |
| static_cast<int>(GetAllCookies(&default_context()).size())); |
| EXPECT_EQ(expected_set_cookie_count, network_delegate.set_cookie_count()); |
| } |
| } |
| |
| INSTANTIATE_TEST_SUITE_P(/* no label */, |
| URLRequestSameSiteCookiesTest, |
| ::testing::Bool()); |
| |
| // Tests that __Secure- cookies can't be set on non-secure origins. |
| TEST_F(URLRequestTest, SecureCookiePrefixOnNonsecureOrigin) { |
| EmbeddedTestServer http_server; |
| RegisterDefaultHandlers(&http_server); |
| EmbeddedTestServer https_server(EmbeddedTestServer::TYPE_HTTPS); |
| https_server.SetSSLConfig(EmbeddedTestServer::CERT_TEST_NAMES); |
| RegisterDefaultHandlers(&https_server); |
| ASSERT_TRUE(http_server.Start()); |
| ASSERT_TRUE(https_server.Start()); |
| |
| // Try to set a Secure __Secure- cookie on http://a.test (non-secure origin). |
| { |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req = CreateFirstPartyRequest( |
| default_context(), |
| http_server.GetURL("a.test", |
| "/set-cookie?__Secure-nonsecure-origin=1;Secure&" |
| "cookienotsecure=1"), |
| &d); |
| req->Start(); |
| d.RunUntilComplete(); |
| EXPECT_EQ(0, default_network_delegate().blocked_annotate_cookies_count()); |
| EXPECT_EQ(0, default_network_delegate().blocked_set_cookie_count()); |
| } |
| |
| // Verify that the __Secure- cookie was not set by checking cookies for |
| // https://a.test (secure origin). |
| { |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req = CreateFirstPartyRequest( |
| default_context(), https_server.GetURL("a.test", "/echoheader?Cookie"), |
| &d); |
| req->Start(); |
| d.RunUntilComplete(); |
| |
| EXPECT_EQ(d.data_received().find("__Secure-nonsecure-origin=1"), |
| std::string::npos); |
| EXPECT_NE(d.data_received().find("cookienotsecure=1"), std::string::npos); |
| EXPECT_EQ(0, default_network_delegate().blocked_annotate_cookies_count()); |
| EXPECT_EQ(0, default_network_delegate().blocked_set_cookie_count()); |
| } |
| } |
| |
| TEST_F(URLRequestTest, SecureCookiePrefixNonsecure) { |
| EmbeddedTestServer https_server(EmbeddedTestServer::TYPE_HTTPS); |
| RegisterDefaultHandlers(&https_server); |
| ASSERT_TRUE(https_server.Start()); |
| |
| // Try to set a non-Secure __Secure- cookie. |
| { |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req = CreateFirstPartyRequest( |
| default_context(), https_server.GetURL("/set-cookie?__Secure-foo=1"), |
| &d); |
| req->Start(); |
| d.RunUntilComplete(); |
| EXPECT_EQ(0, default_network_delegate().set_cookie_count()); |
| EXPECT_EQ(0, default_network_delegate().blocked_annotate_cookies_count()); |
| EXPECT_EQ(0, default_network_delegate().blocked_set_cookie_count()); |
| } |
| |
| // Verify that the cookie is not set. |
| { |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req = CreateFirstPartyRequest( |
| default_context(), https_server.GetURL("/echoheader?Cookie"), &d); |
| req->Start(); |
| d.RunUntilComplete(); |
| |
| EXPECT_EQ(d.data_received().find("__Secure-foo=1"), std::string::npos); |
| EXPECT_EQ(0, default_network_delegate().blocked_annotate_cookies_count()); |
| EXPECT_EQ(0, default_network_delegate().blocked_set_cookie_count()); |
| } |
| } |
| |
| TEST_F(URLRequestTest, SecureCookiePrefixSecure) { |
| EmbeddedTestServer https_server(EmbeddedTestServer::TYPE_HTTPS); |
| RegisterDefaultHandlers(&https_server); |
| ASSERT_TRUE(https_server.Start()); |
| |
| // Try to set a Secure __Secure- cookie. |
| { |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req = CreateFirstPartyRequest( |
| default_context(), |
| https_server.GetURL("/set-cookie?__Secure-bar=1;Secure"), &d); |
| req->Start(); |
| d.RunUntilComplete(); |
| EXPECT_EQ(0, default_network_delegate().blocked_annotate_cookies_count()); |
| EXPECT_EQ(0, default_network_delegate().blocked_set_cookie_count()); |
| } |
| |
| // Verify that the cookie is set. |
| { |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req = CreateFirstPartyRequest( |
| default_context(), https_server.GetURL("/echoheader?Cookie"), &d); |
| req->Start(); |
| d.RunUntilComplete(); |
| |
| EXPECT_NE(d.data_received().find("__Secure-bar=1"), std::string::npos); |
| EXPECT_EQ(0, default_network_delegate().blocked_annotate_cookies_count()); |
| EXPECT_EQ(0, default_network_delegate().blocked_set_cookie_count()); |
| } |
| } |
| |
| // Tests that secure cookies can't be set on non-secure origins if strict secure |
| // cookies are enabled. |
| TEST_F(URLRequestTest, StrictSecureCookiesOnNonsecureOrigin) { |
| EmbeddedTestServer http_server; |
| RegisterDefaultHandlers(&http_server); |
| EmbeddedTestServer https_server(EmbeddedTestServer::TYPE_HTTPS); |
| https_server.SetSSLConfig(EmbeddedTestServer::CERT_TEST_NAMES); |
| RegisterDefaultHandlers(&https_server); |
| ASSERT_TRUE(http_server.Start()); |
| ASSERT_TRUE(https_server.Start()); |
| |
| // Try to set a Secure cookie and a non-Secure cookie from a nonsecure origin. |
| { |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req = CreateFirstPartyRequest( |
| default_context(), |
| http_server.GetURL("a.test", |
| "/set-cookie?nonsecure-origin=1;Secure&" |
| "cookienotsecure=1"), |
| &d); |
| req->Start(); |
| d.RunUntilComplete(); |
| EXPECT_EQ(0, default_network_delegate().blocked_annotate_cookies_count()); |
| EXPECT_EQ(0, default_network_delegate().blocked_set_cookie_count()); |
| } |
| |
| // Verify that the Secure cookie was not set. |
| { |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req = CreateFirstPartyRequest( |
| default_context(), https_server.GetURL("a.test", "/echoheader?Cookie"), |
| &d); |
| req->Start(); |
| d.RunUntilComplete(); |
| |
| EXPECT_EQ(d.data_received().find("nonsecure-origin=1"), std::string::npos); |
| EXPECT_NE(d.data_received().find("cookienotsecure=1"), std::string::npos); |
| EXPECT_EQ(0, default_network_delegate().blocked_annotate_cookies_count()); |
| EXPECT_EQ(0, default_network_delegate().blocked_set_cookie_count()); |
| } |
| } |
| |
| // FixedDateNetworkDelegate swaps out the server's HTTP Date response header |
| // value for the `fixed_date_` member. |
| class FixedDateNetworkDelegate : public TestNetworkDelegate { |
| public: |
| explicit FixedDateNetworkDelegate(base::StringPiece fixed_date) |
| : fixed_date_(fixed_date) {} |
| |
| FixedDateNetworkDelegate(const FixedDateNetworkDelegate&) = delete; |
| FixedDateNetworkDelegate& operator=(const FixedDateNetworkDelegate&) = delete; |
| |
| ~FixedDateNetworkDelegate() override = default; |
| |
| void set_fixed_date(base::StringPiece fixed_date) { |
| fixed_date_ = static_cast<std::string>(fixed_date); |
| } |
| |
| // NetworkDelegate implementation |
| int OnHeadersReceived( |
| URLRequest* request, |
| CompletionOnceCallback callback, |
| const HttpResponseHeaders* original_response_headers, |
| scoped_refptr<HttpResponseHeaders>* override_response_headers, |
| const IPEndPoint& endpoint, |
| absl::optional<GURL>* preserve_fragment_on_redirect_url) override; |
| |
| private: |
| std::string fixed_date_; |
| }; |
| |
| int FixedDateNetworkDelegate::OnHeadersReceived( |
| URLRequest* request, |
| CompletionOnceCallback callback, |
| const HttpResponseHeaders* original_response_headers, |
| scoped_refptr<HttpResponseHeaders>* override_response_headers, |
| const IPEndPoint& endpoint, |
| absl::optional<GURL>* preserve_fragment_on_redirect_url) { |
| *override_response_headers = base::MakeRefCounted<HttpResponseHeaders>( |
| original_response_headers->raw_headers()); |
| |
| (*override_response_headers)->SetHeader("Date", fixed_date_); |
| |
| return TestNetworkDelegate::OnHeadersReceived( |
| request, std::move(callback), original_response_headers, |
| override_response_headers, endpoint, preserve_fragment_on_redirect_url); |
| } |
| |
| // Test that cookie expiration times are adjusted for server/client clock |
| // skew and that we handle incorrect timezone specifier "UTC" in HTTP Date |
| // headers by defaulting to GMT. (crbug.com/135131) |
| TEST_F(URLRequestTest, AcceptClockSkewCookieWithWrongDateTimezone) { |
| HttpTestServer test_server; |
| ASSERT_TRUE(test_server.Start()); |
| |
| auto context_builder = CreateTestURLRequestContextBuilder(); |
| auto& network_delegate = *context_builder->set_network_delegate( |
| std::make_unique<FixedDateNetworkDelegate>("04-Jan-2004 04:09:25 UTC")); |
| auto context = context_builder->Build(); |
| |
| // Set up an expired cookie. |
| { |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req = CreateFirstPartyRequest( |
| *context, |
| test_server.GetURL( |
| "/set-cookie?StillGood=1;expires=Mon,18-Apr-1977,22:50:13,GMT"), |
| &d); |
| req->Start(); |
| d.RunUntilComplete(); |
| } |
| // Verify that the cookie is not set. |
| { |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req = CreateFirstPartyRequest( |
| *context, test_server.GetURL("/echoheader?Cookie"), &d); |
| req->Start(); |
| d.RunUntilComplete(); |
| |
| EXPECT_TRUE(d.data_received().find("StillGood=1") == std::string::npos); |
| } |
| // Set up a cookie with clock skew and "UTC" HTTP Date timezone specifier. |
| { |
| TestDelegate d; |
| network_delegate.set_fixed_date("18-Apr-1977 22:49:13 UTC"); |
| std::unique_ptr<URLRequest> req = CreateFirstPartyRequest( |
| *context, |
| test_server.GetURL( |
| "/set-cookie?StillGood=1;expires=Mon,18-Apr-1977,22:50:13,GMT"), |
| &d); |
| req->Start(); |
| d.RunUntilComplete(); |
| } |
| // Verify that the cookie is set. |
| { |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req = CreateFirstPartyRequest( |
| *context, test_server.GetURL("/echoheader?Cookie"), &d); |
| req->Start(); |
| d.RunUntilComplete(); |
| |
| EXPECT_TRUE(d.data_received().find("StillGood=1") != std::string::npos); |
| } |
| } |
| |
| // Check that it is impossible to change the referrer in the extra headers of |
| // an URLRequest. |
| TEST_F(URLRequestTest, DoNotOverrideReferrer) { |
| HttpTestServer test_server; |
| ASSERT_TRUE(test_server.Start()); |
| |
| // If extra headers contain referer and the request contains a referer, |
| // only the latter shall be respected. |
| { |
| TestDelegate d; |
| std::unique_ptr<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)) { |
| // Needed for NetworkAnonymizationKey to make it down to the socket layer, |
| // for the PKP violation report test. |
| feature_list_.InitAndEnableFeature( |
| net::features::kPartitionConnectionsByNetworkIsolationKey); |
| } |
| |
| protected: |
| // ProtocolHandler for the scheme that's unsafe to redirect to. |
| class NET_EXPORT UnsafeRedirectProtocolHandler |
| : public URLRequestJobFactory::ProtocolHandler { |
| public: |
| UnsafeRedirectProtocolHandler() = default; |
| |
| UnsafeRedirectProtocolHandler(const UnsafeRedirectProtocolHandler&) = |
| delete; |
| UnsafeRedirectProtocolHandler& operator=( |
| const UnsafeRedirectProtocolHandler&) = delete; |
| |
| ~UnsafeRedirectProtocolHandler() override = default; |
| |
| // URLRequestJobFactory::ProtocolHandler implementation: |
| |
| std::unique_ptr<URLRequestJob> CreateJob( |
| URLRequest* request) const override { |
| NOTREACHED(); |
| return nullptr; |
| } |
| |
| bool IsSafeRedirectTarget(const GURL& location) const override { |
| return false; |
| } |
| }; |
| |
| // URLRequestTest interface: |
| void SetUpContextBuilder(URLRequestContextBuilder& builder) override { |
| // Add support for an unsafe scheme to the default URLRequestContext. |
| builder.SetProtocolHandler( |
| "unsafe", std::make_unique<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 = |
| CreateFirstPartyRequest(default_context(), redirect_url, &d); |
| req->set_method(request_method); |
| if (include_data) { |
| req->set_upload(CreateSimpleUploadData(kData)); |
| HttpRequestHeaders headers; |
| headers.SetHeader(HttpRequestHeaders::kContentLength, |
| base::NumberToString(std::size(kData) - 1)); |
| headers.SetHeader(HttpRequestHeaders::kContentType, "text/plain"); |
| req->SetExtraRequestHeaders(headers); |
| } |
| req->Start(); |
| d.RunUntilComplete(); |
| EXPECT_EQ(redirect_method, req->method()); |
| EXPECT_EQ(OK, d.request_status()); |
| if (include_data) { |
| if (request_method == redirect_method) { |
| EXPECT_TRUE(req->extra_request_headers().HasHeader( |
| HttpRequestHeaders::kContentLength)); |
| EXPECT_TRUE(req->extra_request_headers().HasHeader( |
| HttpRequestHeaders::kContentType)); |
| EXPECT_EQ(kData, d.data_received()); |
| } else { |
| EXPECT_FALSE(req->extra_request_headers().HasHeader( |
| HttpRequestHeaders::kContentLength)); |
| EXPECT_FALSE(req->extra_request_headers().HasHeader( |
| HttpRequestHeaders::kContentType)); |
| EXPECT_NE(kData, d.data_received()); |
| } |
| } |
| if (HasFailure()) |
| LOG(WARNING) << "Request method was: " << request_method; |
| } |
| |
| // Requests |redirect_url|, which must return a HTTP 3xx redirect. It's also |
| // used as the initial origin. |
| // |request_method| is the method to use for the initial request. |
| // |redirect_method| is the method that is expected to be used for the second |
| // request, after redirection. |
| // |expected_origin_value| is the expected value for the Origin header after |
| // redirection. If empty, expects that there will be no Origin header. |
| void HTTPRedirectOriginHeaderTest(const GURL& redirect_url, |
| const std::string& request_method, |
| const std::string& redirect_method, |
| const std::string& expected_origin_value) { |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req = |
| CreateFirstPartyRequest(default_context(), redirect_url, &d); |
| req->set_method(request_method); |
| req->SetExtraRequestHeaderByName( |
| HttpRequestHeaders::kOrigin, |
| redirect_url.DeprecatedGetOriginAsURL().spec(), false); |
| req->Start(); |
| |
| d.RunUntilComplete(); |
| |
| EXPECT_EQ(redirect_method, req->method()); |
| // Note that there is no check for request success here because, for |
| // purposes of testing, the request very well may fail. For example, if the |
| // test redirects to an HTTPS server from an HTTP origin, thus it is cross |
| // origin, there is not an HTTPS server in this unit test framework, so the |
| // request would fail. However, that's fine, as long as the request headers |
| // are in order and pass the checks below. |
| if (expected_origin_value.empty()) { |
| EXPECT_FALSE( |
| req->extra_request_headers().HasHeader(HttpRequestHeaders::kOrigin)); |
| } else { |
| std::string origin_header; |
| EXPECT_TRUE(req->extra_request_headers().GetHeader( |
| HttpRequestHeaders::kOrigin, &origin_header)); |
| EXPECT_EQ(expected_origin_value, origin_header); |
| } |
| } |
| |
| void HTTPUploadDataOperationTest(const std::string& method) { |
| const int kMsgSize = 20000; // multiple of 10 |
| const int kIterations = 50; |
| auto uploadBytes = std::make_unique<char[]>(kMsgSize + 1); |
| char* ptr = uploadBytes.get(); |
| char marker = 'a'; |
| for (int idx = 0; idx < kMsgSize / 10; idx++) { |
| memcpy(ptr, "----------", 10); |
| ptr += 10; |
| if (idx % 100 == 0) { |
| ptr--; |
| *ptr++ = marker; |
| if (++marker > 'z') |
| marker = 'a'; |
| } |
| } |
| uploadBytes[kMsgSize] = '\0'; |
| |
| for (int i = 0; i < kIterations; ++i) { |
| TestDelegate d; |
| std::unique_ptr<URLRequest> r(default_context().CreateRequest( |
| test_server_.GetURL("/echo"), DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| r->set_method(method); |
| |
| r->set_upload(CreateSimpleUploadData(uploadBytes.get())); |
| |
| r->Start(); |
| EXPECT_TRUE(r->is_pending()); |
| |
| d.RunUntilComplete(); |
| |
| ASSERT_EQ(1, d.response_started_count()) |
| << "request failed. Error: " << d.request_status(); |
| |
| EXPECT_FALSE(d.received_data_before_response()); |
| EXPECT_EQ(base::StringPiece(uploadBytes.get(), kMsgSize), |
| d.data_received()); |
| } |
| } |
| |
| HttpTestServer* http_test_server() { return &test_server_; } |
| |
| private: |
| base::test::ScopedFeatureList feature_list_; |
| |
| HttpTestServer test_server_; |
| }; |
| |
| namespace { |
| |
| std::unique_ptr<test_server::HttpResponse> HandleRedirectConnect( |
| const test_server::HttpRequest& request) { |
| if (request.headers.find("Host") == request.headers.end() || |
| request.headers.at("Host") != "www.redirect.com" || |
| request.method != test_server::METHOD_CONNECT) { |
| return nullptr; |
| } |
| |
| auto http_response = std::make_unique<test_server::BasicHttpResponse>(); |
| http_response->set_code(HTTP_FOUND); |
| http_response->AddCustomHeader("Location", |
| "http://www.destination.com/foo.js"); |
| return std::move(http_response); |
| } |
| |
| } // namespace |
| |
| // In this unit test, we're using the HTTPTestServer as a proxy server and |
| // issuing a CONNECT request with the magic host name "www.redirect.com". |
| // The EmbeddedTestServer will return a 302 response, which we should not |
| // follow. |
| TEST_F(URLRequestTestHTTP, ProxyTunnelRedirectTest) { |
| http_test_server()->RegisterRequestHandler( |
| base::BindRepeating(&HandleRedirectConnect)); |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| auto context_builder = CreateTestURLRequestContextBuilder(); |
| context_builder->set_proxy_resolution_service( |
| CreateFixedProxyResolutionService( |
| http_test_server()->host_port_pair().ToString())); |
| auto context = context_builder->Build(); |
| |
| TestDelegate d; |
| { |
| std::unique_ptr<URLRequest> r(context->CreateRequest( |
| GURL("https://www.redirect.com/"), DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| r->Start(); |
| EXPECT_TRUE(r->is_pending()); |
| |
| d.RunUntilComplete(); |
| |
| // The proxy server should be set before failure. |
| EXPECT_EQ(ProxyServer(ProxyServer::SCHEME_HTTP, |
| http_test_server()->host_port_pair()), |
| r->proxy_server()); |
| EXPECT_EQ(ERR_TUNNEL_CONNECTION_FAILED, d.request_status()); |
| EXPECT_EQ(1, d.response_started_count()); |
| // We should not have followed the redirect. |
| EXPECT_EQ(0, d.received_redirect_count()); |
| } |
| } |
| |
| // This is the same as the previous test, but checks that the network delegate |
| // registers the error. |
| TEST_F(URLRequestTestHTTP, NetworkDelegateTunnelConnectionFailed) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| auto context_builder = CreateTestURLRequestContextBuilder(); |
| context_builder->set_proxy_resolution_service( |
| CreateFixedProxyResolutionService( |
| http_test_server()->host_port_pair().ToString())); |
| auto& network_delegate = *context_builder->set_network_delegate( |
| std::make_unique<TestNetworkDelegate>()); |
| auto context = context_builder->Build(); |
| |
| TestDelegate d; |
| { |
| std::unique_ptr<URLRequest> r(context->CreateRequest( |
| GURL("https://www.redirect.com/"), DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| r->Start(); |
| EXPECT_TRUE(r->is_pending()); |
| |
| d.RunUntilComplete(); |
| |
| // The proxy server should be set before failure. |
| EXPECT_EQ(ProxyServer(ProxyServer::SCHEME_HTTP, |
| http_test_server()->host_port_pair()), |
| r->proxy_server()); |
| EXPECT_EQ(1, d.response_started_count()); |
| EXPECT_EQ(ERR_TUNNEL_CONNECTION_FAILED, d.request_status()); |
| // We should not have followed the redirect. |
| EXPECT_EQ(0, d.received_redirect_count()); |
| |
| EXPECT_EQ(1, network_delegate.error_count()); |
| EXPECT_THAT(network_delegate.last_error(), |
| IsError(ERR_TUNNEL_CONNECTION_FAILED)); |
| } |
| } |
| |
| // Tests that we can block and asynchronously return OK in various stages. |
| TEST_F(URLRequestTestHTTP, NetworkDelegateBlockAsynchronously) { |
| static const BlockingNetworkDelegate::Stage blocking_stages[] = { |
| BlockingNetworkDelegate::ON_BEFORE_URL_REQUEST, |
| BlockingNetworkDelegate::ON_BEFORE_SEND_HEADERS, |
| BlockingNetworkDelegate::ON_HEADERS_RECEIVED}; |
| |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| TestDelegate d; |
| auto context_builder = CreateTestURLRequestContextBuilder(); |
| auto& network_delegate = *context_builder->set_network_delegate( |
| std::make_unique<BlockingNetworkDelegate>( |
| BlockingNetworkDelegate::USER_CALLBACK)); |
| network_delegate.set_block_on( |
| BlockingNetworkDelegate::ON_BEFORE_URL_REQUEST | |
| BlockingNetworkDelegate::ON_BEFORE_SEND_HEADERS | |
| BlockingNetworkDelegate::ON_HEADERS_RECEIVED); |
| auto context = context_builder->Build(); |
| |
| { |
| std::unique_ptr<URLRequest> r(context->CreateRequest( |
| http_test_server()->GetURL("/defaultresponse"), DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| |
| r->Start(); |
| for (auto stage : blocking_stages) { |
| network_delegate.RunUntilBlocked(); |
| EXPECT_EQ(stage, network_delegate.stage_blocked_for_callback()); |
| network_delegate.DoCallback(OK); |
| } |
| d.RunUntilComplete(); |
| EXPECT_EQ(200, r->GetResponseCode()); |
| EXPECT_EQ(OK, d.request_status()); |
| EXPECT_EQ(1, network_delegate.created_requests()); |
| EXPECT_EQ(0, network_delegate.destroyed_requests()); |
| } |
| EXPECT_EQ(1, network_delegate.destroyed_requests()); |
| } |
| |
| // Tests that the network delegate can block and cancel a request. |
| TEST_F(URLRequestTestHTTP, NetworkDelegateCancelRequest) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| TestDelegate d; |
| auto context_builder = CreateTestURLRequestContextBuilder(); |
| context_builder->set_proxy_resolution_service( |
| CreateFixedProxyResolutionService( |
| http_test_server()->host_port_pair().ToString())); |
| auto& network_delegate = *context_builder->set_network_delegate( |
| std::make_unique<BlockingNetworkDelegate>( |
| BlockingNetworkDelegate::AUTO_CALLBACK)); |
| auto context = context_builder->Build(); |
| |
| network_delegate.set_block_on(BlockingNetworkDelegate::ON_BEFORE_URL_REQUEST); |
| network_delegate.set_retval(ERR_EMPTY_RESPONSE); |
| { |
| std::unique_ptr<URLRequest> r(context->CreateRequest( |
| http_test_server()->GetURL("/"), DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| |
| r->Start(); |
| d.RunUntilComplete(); |
| |
| // The proxy server is not set before cancellation. |
| EXPECT_FALSE(r->proxy_server().is_valid()); |
| EXPECT_EQ(ERR_EMPTY_RESPONSE, d.request_status()); |
| EXPECT_EQ(1, network_delegate.created_requests()); |
| EXPECT_EQ(0, network_delegate.destroyed_requests()); |
| } |
| EXPECT_EQ(1, network_delegate.destroyed_requests()); |
| } |
| |
| // Helper function for NetworkDelegateCancelRequestAsynchronously and |
| // NetworkDelegateCancelRequestSynchronously. Sets up a blocking network |
| // delegate operating in |block_mode| and a request for |url|. It blocks the |
| // request in |stage| and cancels it with ERR_BLOCKED_BY_CLIENT. |
| void NetworkDelegateCancelRequest(BlockingNetworkDelegate::BlockMode block_mode, |
| BlockingNetworkDelegate::Stage stage, |
| const GURL& url) { |
| TestDelegate d; |
| auto context_builder = CreateTestURLRequestContextBuilder(); |
| auto& network_delegate = *context_builder->set_network_delegate( |
| std::make_unique<BlockingNetworkDelegate>(block_mode)); |
| network_delegate.set_retval(ERR_BLOCKED_BY_CLIENT); |
| network_delegate.set_block_on(stage); |
| |
| auto context = context_builder->Build(); |
| { |
| std::unique_ptr<URLRequest> r(context->CreateRequest( |
| url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| |
| r->Start(); |
| d.RunUntilComplete(); |
| |
| // The proxy server is not set before cancellation. |
| if (stage == BlockingNetworkDelegate::ON_BEFORE_URL_REQUEST || |
| stage == BlockingNetworkDelegate::ON_BEFORE_SEND_HEADERS) { |
| EXPECT_FALSE(r->proxy_server().is_valid()); |
| } else if (stage == BlockingNetworkDelegate::ON_HEADERS_RECEIVED) { |
| EXPECT_TRUE(r->proxy_server().is_direct()); |
| } else { |
| NOTREACHED(); |
| } |
| EXPECT_EQ(ERR_BLOCKED_BY_CLIENT, d.request_status()); |
| EXPECT_EQ(1, network_delegate.created_requests()); |
| EXPECT_EQ(0, network_delegate.destroyed_requests()); |
| } |
| EXPECT_EQ(1, network_delegate.destroyed_requests()); |
| } |
| |
| // The following 3 tests check that the network delegate can cancel a request |
| // synchronously in various stages of the request. |
| TEST_F(URLRequestTestHTTP, NetworkDelegateCancelRequestSynchronously1) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| NetworkDelegateCancelRequest(BlockingNetworkDelegate::SYNCHRONOUS, |
| BlockingNetworkDelegate::ON_BEFORE_URL_REQUEST, |
| http_test_server()->GetURL("/")); |
| } |
| |
| TEST_F(URLRequestTestHTTP, NetworkDelegateCancelRequestSynchronously2) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| NetworkDelegateCancelRequest(BlockingNetworkDelegate::SYNCHRONOUS, |
| BlockingNetworkDelegate::ON_BEFORE_SEND_HEADERS, |
| http_test_server()->GetURL("/")); |
| } |
| |
| TEST_F(URLRequestTestHTTP, NetworkDelegateCancelRequestSynchronously3) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| NetworkDelegateCancelRequest(BlockingNetworkDelegate::SYNCHRONOUS, |
| BlockingNetworkDelegate::ON_HEADERS_RECEIVED, |
| http_test_server()->GetURL("/")); |
| } |
| |
| // The following 3 tests check that the network delegate can cancel a request |
| // asynchronously in various stages of the request. |
| TEST_F(URLRequestTestHTTP, NetworkDelegateCancelRequestAsynchronously1) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| NetworkDelegateCancelRequest(BlockingNetworkDelegate::AUTO_CALLBACK, |
| BlockingNetworkDelegate::ON_BEFORE_URL_REQUEST, |
| http_test_server()->GetURL("/")); |
| } |
| |
| TEST_F(URLRequestTestHTTP, NetworkDelegateCancelRequestAsynchronously2) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| NetworkDelegateCancelRequest(BlockingNetworkDelegate::AUTO_CALLBACK, |
| BlockingNetworkDelegate::ON_BEFORE_SEND_HEADERS, |
| http_test_server()->GetURL("/")); |
| } |
| |
| TEST_F(URLRequestTestHTTP, NetworkDelegateCancelRequestAsynchronously3) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| NetworkDelegateCancelRequest(BlockingNetworkDelegate::AUTO_CALLBACK, |
| BlockingNetworkDelegate::ON_HEADERS_RECEIVED, |
| http_test_server()->GetURL("/")); |
| } |
| |
| // Tests that the network delegate can block and redirect a request to a new |
| // URL. |
| TEST_F(URLRequestTestHTTP, NetworkDelegateRedirectRequest) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| TestDelegate d; |
| auto context_builder = CreateTestURLRequestContextBuilder(); |
| context_builder->set_proxy_resolution_service( |
| CreateFixedProxyResolutionService( |
| http_test_server()->host_port_pair().ToString())); |
| auto& network_delegate = *context_builder->set_network_delegate( |
| std::make_unique<BlockingNetworkDelegate>( |
| BlockingNetworkDelegate::AUTO_CALLBACK)); |
| auto context = context_builder->Build(); |
| |
| GURL redirect_url("http://does.not.resolve.test/simple.html"); |
| network_delegate.set_redirect_url(redirect_url); |
| { |
| GURL original_url("http://does.not.resolve.test/defaultresponse"); |
| std::unique_ptr<URLRequest> r(context->CreateRequest( |
| original_url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| |
| // Quit after hitting the redirect, so can check the headers. |
| r->Start(); |
| d.RunUntilRedirect(); |
| |
| // Check headers from URLRequestJob. |
| EXPECT_EQ(307, r->GetResponseCode()); |
| EXPECT_EQ(307, r->response_headers()->response_code()); |
| std::string location; |
| ASSERT_TRUE( |
| r->response_headers()->EnumerateHeader(nullptr, "Location", &location)); |
| EXPECT_EQ(redirect_url, GURL(location)); |
| |
| // Let the request finish. |
| r->FollowDeferredRedirect(absl::nullopt /* removed_headers */, |
| absl::nullopt /* modified_headers */); |
| d.RunUntilComplete(); |
| EXPECT_EQ(OK, d.request_status()); |
| EXPECT_EQ(ProxyServer(ProxyServer::SCHEME_HTTP, |
| http_test_server()->host_port_pair()), |
| r->proxy_server()); |
| EXPECT_EQ(OK, d.request_status()); |
| EXPECT_EQ(redirect_url, r->url()); |
| EXPECT_EQ(original_url, r->original_url()); |
| EXPECT_EQ(2U, r->url_chain().size()); |
| EXPECT_EQ(1, network_delegate.created_requests()); |
| EXPECT_EQ(0, network_delegate.destroyed_requests()); |
| } |
| EXPECT_EQ(1, network_delegate.destroyed_requests()); |
| } |
| |
| // Tests that the network delegate can block and redirect a request to a new |
| // URL by setting a redirect_url and returning in OnBeforeURLRequest directly. |
| TEST_F(URLRequestTestHTTP, NetworkDelegateRedirectRequestSynchronously) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| TestDelegate d; |
| auto context_builder = CreateTestURLRequestContextBuilder(); |
| context_builder->set_proxy_resolution_service( |
| CreateFixedProxyResolutionService( |
| http_test_server()->host_port_pair().ToString())); |
| auto& network_delegate = *context_builder->set_network_delegate( |
| std::make_unique<BlockingNetworkDelegate>( |
| BlockingNetworkDelegate::SYNCHRONOUS)); |
| auto context = context_builder->Build(); |
| |
| GURL redirect_url("http://does.not.resolve.test/simple.html"); |
| network_delegate.set_redirect_url(redirect_url); |
| { |
| GURL original_url("http://does.not.resolve.test/defaultresponse"); |
| std::unique_ptr<URLRequest> r(context->CreateRequest( |
| original_url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| |
| // Quit after hitting the redirect, so can check the headers. |
| r->Start(); |
| d.RunUntilRedirect(); |
| |
| // Check headers from URLRequestJob. |
| EXPECT_EQ(307, r->GetResponseCode()); |
| EXPECT_EQ(307, r->response_headers()->response_code()); |
| std::string location; |
| ASSERT_TRUE( |
| r->response_headers()->EnumerateHeader(nullptr, "Location", &location)); |
| EXPECT_EQ(redirect_url, GURL(location)); |
| |
| // Let the request finish. |
| r->FollowDeferredRedirect(absl::nullopt /* removed_headers */, |
| absl::nullopt /* modified_headers */); |
| d.RunUntilComplete(); |
| |
| EXPECT_EQ(OK, d.request_status()); |
| EXPECT_EQ(ProxyServer(ProxyServer::SCHEME_HTTP, |
| http_test_server()->host_port_pair()), |
| r->proxy_server()); |
| EXPECT_EQ(OK, d.request_status()); |
| EXPECT_EQ(redirect_url, r->url()); |
| EXPECT_EQ(original_url, r->original_url()); |
| EXPECT_EQ(2U, r->url_chain().size()); |
| EXPECT_EQ(1, network_delegate.created_requests()); |
| EXPECT_EQ(0, network_delegate.destroyed_requests()); |
| } |
| EXPECT_EQ(1, network_delegate.destroyed_requests()); |
| } |
| |
| // Tests that redirects caused by the network delegate preserve POST data. |
| TEST_F(URLRequestTestHTTP, NetworkDelegateRedirectRequestPost) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| const char kData[] = "hello world"; |
| |
| TestDelegate d; |
| auto context_builder = CreateTestURLRequestContextBuilder(); |
| auto& network_delegate = *context_builder->set_network_delegate( |
| std::make_unique<BlockingNetworkDelegate>( |
| BlockingNetworkDelegate::AUTO_CALLBACK)); |
| network_delegate.set_block_on(BlockingNetworkDelegate::ON_BEFORE_URL_REQUEST); |
| GURL redirect_url(http_test_server()->GetURL("/echo")); |
| network_delegate.set_redirect_url(redirect_url); |
| |
| auto context = context_builder->Build(); |
| |
| { |
| GURL original_url(http_test_server()->GetURL("/defaultresponse")); |
| std::unique_ptr<URLRequest> r(context->CreateRequest( |
| original_url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| r->set_method("POST"); |
| r->set_upload(CreateSimpleUploadData(kData)); |
| HttpRequestHeaders headers; |
| headers.SetHeader(HttpRequestHeaders::kContentLength, |
| base::NumberToString(std::size(kData) - 1)); |
| r->SetExtraRequestHeaders(headers); |
| |
| // Quit after hitting the redirect, so can check the headers. |
| r->Start(); |
| d.RunUntilRedirect(); |
| |
| // Check headers from URLRequestJob. |
| EXPECT_EQ(307, r->GetResponseCode()); |
| EXPECT_EQ(307, r->response_headers()->response_code()); |
| std::string location; |
| ASSERT_TRUE( |
| r->response_headers()->EnumerateHeader(nullptr, "Location", &location)); |
| EXPECT_EQ(redirect_url, GURL(location)); |
| |
| // Let the request finish. |
| r->FollowDeferredRedirect(absl::nullopt /* removed_headers */, |
| absl::nullopt /* modified_headers */); |
| d.RunUntilComplete(); |
| |
| EXPECT_EQ(OK, d.request_status()); |
| EXPECT_EQ(redirect_url, r->url()); |
| EXPECT_EQ(original_url, r->original_url()); |
| EXPECT_EQ(2U, r->url_chain().size()); |
| EXPECT_EQ(1, network_delegate.created_requests()); |
| EXPECT_EQ(0, network_delegate.destroyed_requests()); |
| EXPECT_EQ("POST", r->method()); |
| EXPECT_EQ(kData, d.data_received()); |
| } |
| EXPECT_EQ(1, network_delegate.destroyed_requests()); |
| } |
| |
| // Tests that the network delegate can block and redirect a request to a new |
| // URL during OnHeadersReceived. |
| TEST_F(URLRequestTestHTTP, NetworkDelegateRedirectRequestOnHeadersReceived) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| TestDelegate d; |
| auto context_builder = CreateTestURLRequestContextBuilder(); |
| context_builder->set_proxy_resolution_service( |
| CreateFixedProxyResolutionService( |
| http_test_server()->host_port_pair().ToString())); |
| auto& network_delegate = *context_builder->set_network_delegate( |
| std::make_unique<BlockingNetworkDelegate>( |
| BlockingNetworkDelegate::AUTO_CALLBACK)); |
| auto context = context_builder->Build(); |
| |
| network_delegate.set_block_on(BlockingNetworkDelegate::ON_HEADERS_RECEIVED); |
| GURL redirect_url("http://does.not.resolve.test/simple.html"); |
| network_delegate.set_redirect_on_headers_received_url(redirect_url); |
| { |
| GURL original_url("http://does.not.resolve.test/defaultresponse"); |
| std::unique_ptr<URLRequest> r(context->CreateRequest( |
| original_url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| |
| r->Start(); |
| d.RunUntilComplete(); |
| |
| EXPECT_EQ(OK, d.request_status()); |
| EXPECT_EQ(ProxyServer(ProxyServer::SCHEME_HTTP, |
| http_test_server()->host_port_pair()), |
| r->proxy_server()); |
| EXPECT_EQ(OK, d.request_status()); |
| EXPECT_EQ(redirect_url, r->url()); |
| EXPECT_EQ(original_url, r->original_url()); |
| EXPECT_EQ(2U, r->url_chain().size()); |
| EXPECT_EQ(2, network_delegate.created_requests()); |
| EXPECT_EQ(0, network_delegate.destroyed_requests()); |
| } |
| EXPECT_EQ(1, network_delegate.destroyed_requests()); |
| } |
| |
| // Tests that the network delegate can synchronously complete OnAuthRequired |
| // by taking no action. This indicates that the NetworkDelegate does not want to |
| // handle the challenge, and is passing the buck along to the |
| // URLRequest::Delegate. |
| TEST_F(URLRequestTestHTTP, NetworkDelegateOnAuthRequiredSyncNoAction) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| TestDelegate d; |
| auto context_builder = CreateTestURLRequestContextBuilder(); |
| auto& network_delegate = *context_builder->set_network_delegate( |
| std::make_unique<BlockingNetworkDelegate>( |
| BlockingNetworkDelegate::SYNCHRONOUS)); |
| auto context = context_builder->Build(); |
| |
| d.set_credentials(AuthCredentials(kUser, kSecret)); |
| |
| { |
| GURL url(http_test_server()->GetURL("/auth-basic")); |
| std::unique_ptr<URLRequest> r(context->CreateRequest( |
| url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| r->Start(); |
| |
| d.RunUntilComplete(); |
| |
| EXPECT_EQ(OK, d.request_status()); |
| EXPECT_EQ(200, r->GetResponseCode()); |
| EXPECT_TRUE(d.auth_required_called()); |
| EXPECT_EQ(1, network_delegate.created_requests()); |
| EXPECT_EQ(0, network_delegate.destroyed_requests()); |
| } |
| EXPECT_EQ(1, network_delegate.destroyed_requests()); |
| } |
| |
| // Tests that NetworkDelegate header overrides from the 401 response do not |
| // affect the 200 response. This is a regression test for |
| // https://crbug.com/801237. |
| TEST_F(URLRequestTestHTTP, NetworkDelegateOverrideHeadersWithAuth) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| TestDelegate d; |
| d.set_credentials(AuthCredentials(kUser, kSecret)); |
| default_network_delegate().set_add_header_to_first_response(true); |
| |
| { |
| GURL url(http_test_server()->GetURL("/auth-basic")); |
| std::unique_ptr<URLRequest> r(default_context().CreateRequest( |
| url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| r->Start(); |
| |
| d.RunUntilComplete(); |
| |
| EXPECT_EQ(OK, d.request_status()); |
| EXPECT_EQ(200, r->GetResponseCode()); |
| EXPECT_TRUE(d.auth_required_called()); |
| EXPECT_FALSE(r->response_headers()->HasHeader("X-Network-Delegate")); |
| } |
| |
| { |
| GURL url(http_test_server()->GetURL("/defaultresponse")); |
| std::unique_ptr<URLRequest> r(default_context().CreateRequest( |
| url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| r->Start(); |
| |
| d.RunUntilComplete(); |
| |
| // Check that set_add_header_to_first_response normally adds a header. |
| EXPECT_EQ(OK, d.request_status()); |
| EXPECT_EQ(200, r->GetResponseCode()); |
| EXPECT_TRUE(r->response_headers()->HasHeader("X-Network-Delegate")); |
| } |
| } |
| |
| // Tests that we can handle when a network request was canceled while we were |
| // waiting for the network delegate. |
| // Part 1: Request is cancelled while waiting for OnBeforeURLRequest callback. |
| TEST_F(URLRequestTestHTTP, NetworkDelegateCancelWhileWaiting1) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| TestDelegate d; |
| auto context_builder = CreateTestURLRequestContextBuilder(); |
| auto& network_delegate = *context_builder->set_network_delegate( |
| std::make_unique<BlockingNetworkDelegate>( |
| BlockingNetworkDelegate::USER_CALLBACK)); |
| network_delegate.set_block_on(BlockingNetworkDelegate::ON_BEFORE_URL_REQUEST); |
| auto context = context_builder->Build(); |
| |
| { |
| std::unique_ptr<URLRequest> r(context->CreateRequest( |
| http_test_server()->GetURL("/"), DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| |
| r->Start(); |
| network_delegate.RunUntilBlocked(); |
| EXPECT_EQ(BlockingNetworkDelegate::ON_BEFORE_URL_REQUEST, |
| network_delegate.stage_blocked_for_callback()); |
| EXPECT_EQ(0, network_delegate.completed_requests()); |
| // Cancel before callback. |
| r->Cancel(); |
| // Ensure that network delegate is notified. |
| EXPECT_EQ(1, network_delegate.completed_requests()); |
| EXPECT_EQ(1, network_delegate.canceled_requests()); |
| EXPECT_EQ(1, network_delegate.created_requests()); |
| EXPECT_EQ(0, network_delegate.destroyed_requests()); |
| } |
| EXPECT_EQ(1, network_delegate.destroyed_requests()); |
| } |
| |
| // Tests that we can handle when a network request was canceled while we were |
| // waiting for the network delegate. |
| // Part 2: Request is cancelled while waiting for OnBeforeStartTransaction |
| // callback. |
| TEST_F(URLRequestTestHTTP, NetworkDelegateCancelWhileWaiting2) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| TestDelegate d; |
| auto context_builder = CreateTestURLRequestContextBuilder(); |
| auto& network_delegate = *context_builder->set_network_delegate( |
| std::make_unique<BlockingNetworkDelegate>( |
| BlockingNetworkDelegate::USER_CALLBACK)); |
| network_delegate.set_block_on( |
| BlockingNetworkDelegate::ON_BEFORE_SEND_HEADERS); |
| auto context = context_builder->Build(); |
| |
| { |
| std::unique_ptr<URLRequest> r(context->CreateRequest( |
| http_test_server()->GetURL("/"), DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| |
| r->Start(); |
| network_delegate.RunUntilBlocked(); |
| EXPECT_EQ(BlockingNetworkDelegate::ON_BEFORE_SEND_HEADERS, |
| network_delegate.stage_blocked_for_callback()); |
| EXPECT_EQ(0, network_delegate.completed_requests()); |
| // Cancel before callback. |
| r->Cancel(); |
| // Ensure that network delegate is notified. |
| EXPECT_EQ(1, network_delegate.completed_requests()); |
| EXPECT_EQ(1, network_delegate.canceled_requests()); |
| EXPECT_EQ(1, network_delegate.created_requests()); |
| EXPECT_EQ(0, network_delegate.destroyed_requests()); |
| } |
| EXPECT_EQ(1, network_delegate.destroyed_requests()); |
| } |
| |
| // Tests that we can handle when a network request was canceled while we were |
| // waiting for the network delegate. |
| // Part 3: Request is cancelled while waiting for OnHeadersReceived callback. |
| TEST_F(URLRequestTestHTTP, NetworkDelegateCancelWhileWaiting3) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| TestDelegate d; |
| auto context_builder = CreateTestURLRequestContextBuilder(); |
| auto& network_delegate = *context_builder->set_network_delegate( |
| std::make_unique<BlockingNetworkDelegate>( |
| BlockingNetworkDelegate::USER_CALLBACK)); |
| network_delegate.set_block_on(BlockingNetworkDelegate::ON_HEADERS_RECEIVED); |
| auto context = context_builder->Build(); |
| |
| { |
| std::unique_ptr<URLRequest> r(context->CreateRequest( |
| http_test_server()->GetURL("/"), DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| |
| r->Start(); |
| network_delegate.RunUntilBlocked(); |
| EXPECT_EQ(BlockingNetworkDelegate::ON_HEADERS_RECEIVED, |
| network_delegate.stage_blocked_for_callback()); |
| EXPECT_EQ(0, network_delegate.completed_requests()); |
| // Cancel before callback. |
| r->Cancel(); |
| // Ensure that network delegate is notified. |
| EXPECT_EQ(1, network_delegate.completed_requests()); |
| EXPECT_EQ(1, network_delegate.canceled_requests()); |
| EXPECT_EQ(1, network_delegate.created_requests()); |
| EXPECT_EQ(0, network_delegate.destroyed_requests()); |
| } |
| EXPECT_EQ(1, network_delegate.destroyed_requests()); |
| } |
| |
| namespace { |
| |
| std::unique_ptr<test_server::HttpResponse> HandleServerAuthConnect( |
| const test_server::HttpRequest& request) { |
| if (request.headers.find("Host") == request.headers.end() || |
| request.headers.at("Host") != "www.server-auth.com" || |
| request.method != test_server::METHOD_CONNECT) { |
| return nullptr; |
| } |
| |
| auto http_response = std::make_unique<test_server::BasicHttpResponse>(); |
| http_response->set_code(HTTP_UNAUTHORIZED); |
| http_response->AddCustomHeader("WWW-Authenticate", |
| "Basic realm=\"WallyWorld\""); |
| return http_response; |
| } |
| |
| } // namespace |
| |
| // In this unit test, we're using the EmbeddedTestServer as a proxy server and |
| // issuing a CONNECT request with the magic host name "www.server-auth.com". |
| // The EmbeddedTestServer will return a 401 response, which we should balk at. |
| TEST_F(URLRequestTestHTTP, UnexpectedServerAuthTest) { |
| http_test_server()->RegisterRequestHandler( |
| base::BindRepeating(&HandleServerAuthConnect)); |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| auto context_builder = CreateTestURLRequestContextBuilder(); |
| context_builder->set_proxy_resolution_service( |
| CreateFixedProxyResolutionService( |
| http_test_server()->host_port_pair().ToString())); |
| auto context = context_builder->Build(); |
| |
| TestDelegate d; |
| { |
| std::unique_ptr<URLRequest> r(context->CreateRequest( |
| GURL("https://www.server-auth.com/"), DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| |
| r->Start(); |
| EXPECT_TRUE(r->is_pending()); |
| |
| d.RunUntilComplete(); |
| |
| // The proxy server should be set before failure. |
| EXPECT_EQ(ProxyServer(ProxyServer::SCHEME_HTTP, |
| http_test_server()->host_port_pair()), |
| r->proxy_server()); |
| EXPECT_EQ(ERR_TUNNEL_CONNECTION_FAILED, d.request_status()); |
| } |
| } |
| |
| TEST_F(URLRequestTestHTTP, GetTest_NoCache) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| TestDelegate d; |
| { |
| std::unique_ptr<URLRequest> r(default_context().CreateRequest( |
| http_test_server()->GetURL("/defaultresponse"), DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| |
| r->Start(); |
| EXPECT_TRUE(r->is_pending()); |
| |
| d.RunUntilComplete(); |
| |
| EXPECT_EQ(1, d.response_started_count()); |
| EXPECT_FALSE(d.received_data_before_response()); |
| EXPECT_NE(0, d.bytes_received()); |
| EXPECT_EQ(http_test_server()->host_port_pair().host(), |
| r->GetResponseRemoteEndpoint().ToStringWithoutPort()); |
| EXPECT_EQ(http_test_server()->host_port_pair().port(), |
| r->GetResponseRemoteEndpoint().port()); |
| |
| // TODO(eroman): Add back the NetLog tests... |
| } |
| } |
| |
| TEST_F(URLRequestTestHTTP, GetTest) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| TestDelegate d; |
| { |
| std::unique_ptr<URLRequest> r(default_context().CreateRequest( |
| http_test_server()->GetURL("/defaultresponse"), DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| |
| r->Start(); |
| EXPECT_TRUE(r->is_pending()); |
| |
| d.RunUntilComplete(); |
| |
| EXPECT_EQ(1, d.response_started_count()); |
| EXPECT_FALSE(d.received_data_before_response()); |
| EXPECT_NE(0, d.bytes_received()); |
| EXPECT_EQ(http_test_server()->host_port_pair().host(), |
| r->GetResponseRemoteEndpoint().ToStringWithoutPort()); |
| EXPECT_EQ(http_test_server()->host_port_pair().port(), |
| r->GetResponseRemoteEndpoint().port()); |
| } |
| } |
| |
| TEST_F(URLRequestTestHTTP, GetTestLoadTiming) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| TestDelegate d; |
| { |
| std::unique_ptr<URLRequest> r(default_context().CreateRequest( |
| http_test_server()->GetURL("/defaultresponse"), DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| |
| r->Start(); |
| EXPECT_TRUE(r->is_pending()); |
| |
| d.RunUntilComplete(); |
| |
| LoadTimingInfo load_timing_info; |
| r->GetLoadTimingInfo(&load_timing_info); |
| TestLoadTimingNotReused(load_timing_info, CONNECT_TIMING_HAS_DNS_TIMES); |
| |
| EXPECT_EQ(1, d.response_started_count()); |
| EXPECT_FALSE(d.received_data_before_response()); |
| EXPECT_NE(0, d.bytes_received()); |
| EXPECT_EQ(http_test_server()->host_port_pair().host(), |
| r->GetResponseRemoteEndpoint().ToStringWithoutPort()); |
| EXPECT_EQ(http_test_server()->host_port_pair().port(), |
| r->GetResponseRemoteEndpoint().port()); |
| } |
| } |
| |
| namespace { |
| |
| // Sends the correct Content-Length matching the compressed length. |
| const char kZippedContentLengthCompressed[] = "C"; |
| // Sends an incorrect Content-Length matching the uncompressed length. |
| const char kZippedContentLengthUncompressed[] = "U"; |
| // Sends an incorrect Content-Length shorter than the compressed length. |
| const char kZippedContentLengthShort[] = "S"; |
| // Sends an incorrect Content-Length between the compressed and uncompressed |
| // lengths. |
| const char kZippedContentLengthMedium[] = "M"; |
| // Sends an incorrect Content-Length larger than both compressed and |
| // uncompressed lengths. |
| const char kZippedContentLengthLong[] = "L"; |
| |
| // Sends |compressed_content| which, when decoded with deflate, should have |
| // length |uncompressed_length|. The Content-Length header will be sent based on |
| // which of the constants above is sent in the query string. |
| std::unique_ptr<test_server::HttpResponse> HandleZippedRequest( |
| const std::string& compressed_content, |
| size_t uncompressed_length, |
| const test_server::HttpRequest& request) { |
| GURL url = request.GetURL(); |
| if (url.path_piece() != "/compressedfiles/BullRunSpeech.txt") |
| return nullptr; |
| |
| size_t length; |
| if (url.query_piece() == kZippedContentLengthCompressed) { |
| length = compressed_content.size(); |
| } else if (url.query_piece() == kZippedContentLengthUncompressed) { |
| length = uncompressed_length; |
| } else if (url.query_piece() == kZippedContentLengthShort) { |
| length = compressed_content.size() / 2; |
| } else if (url.query_piece() == kZippedContentLengthMedium) { |
| length = (compressed_content.size() + uncompressed_length) / 2; |
| } else if (url.query_piece() == kZippedContentLengthLong) { |
| length = compressed_content.size() + uncompressed_length; |
| } else { |
| return nullptr; |
| } |
| |
| std::string headers = "HTTP/1.1 200 OK\r\n"; |
| headers += "Content-Encoding: deflate\r\n"; |
| base::StringAppendF(&headers, "Content-Length: %zu\r\n", length); |
| return std::make_unique<test_server::RawHttpResponse>(headers, |
| compressed_content); |
| } |
| |
| } // namespace |
| |
| TEST_F(URLRequestTestHTTP, GetZippedTest) { |
| base::FilePath file_path; |
| base::PathService::Get(base::DIR_SOURCE_ROOT, &file_path); |
| file_path = file_path.Append(kTestFilePath); |
| std::string expected_content, compressed_content; |
| ASSERT_TRUE(base::ReadFileToString( |
| file_path.Append(FILE_PATH_LITERAL("BullRunSpeech.txt")), |
| &expected_content)); |
| // This file is the output of the Python zlib.compress function on |
| // |expected_content|. |
| ASSERT_TRUE(base::ReadFileToString( |
| file_path.Append(FILE_PATH_LITERAL("BullRunSpeech.txt.deflate")), |
| &compressed_content)); |
| |
| http_test_server()->RegisterRequestHandler(base::BindRepeating( |
| &HandleZippedRequest, compressed_content, expected_content.size())); |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| static const struct { |
| const char* parameter; |
| bool expect_success; |
| } kTests[] = { |
| // Sending the compressed Content-Length is correct. |
| {kZippedContentLengthCompressed, true}, |
| // Sending the uncompressed Content-Length is incorrect, but we accept it |
| // to workaround some broken servers. |
| {kZippedContentLengthUncompressed, true}, |
| // Sending too long of Content-Length is rejected. |
| {kZippedContentLengthLong, false}, |
| {kZippedContentLengthMedium, false}, |
| // Sending too short of Content-Length successfully fetches a response |
| // body, but it will be truncated. |
| {kZippedContentLengthShort, true}, |
| }; |
| |
| for (const auto& test : kTests) { |
| SCOPED_TRACE(test.parameter); |
| TestDelegate d; |
| std::string test_file = base::StringPrintf( |
| "/compressedfiles/BullRunSpeech.txt?%s", test.parameter); |
| |
| auto context_builder = CreateTestURLRequestContextBuilder(); |
| context_builder->set_network_delegate( |
| std::make_unique<TestNetworkDelegate>()); |
| auto context = context_builder->Build(); |
| |
| std::unique_ptr<URLRequest> r(context->CreateRequest( |
| http_test_server()->GetURL(test_file), DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| r->Start(); |
| EXPECT_TRUE(r->is_pending()); |
| |
| d.RunUntilComplete(); |
| |
| EXPECT_EQ(1, d.response_started_count()); |
| EXPECT_FALSE(d.received_data_before_response()); |
| VLOG(1) << " Received " << d.bytes_received() << " bytes" |
| << " error = " << d.request_status(); |
| if (test.expect_success) { |
| EXPECT_EQ(OK, d.request_status()) |
| << " Parameter = \"" << test_file << "\""; |
| if (strcmp(test.parameter, kZippedContentLengthShort) == 0) { |
| // When content length is smaller than both compressed length and |
| // uncompressed length, HttpStreamParser might not read the full |
| // response body. |
| EXPECT_EQ(expected_content.substr(0, d.data_received().size()), |
| d.data_received()); |
| } else { |
| EXPECT_EQ(expected_content, d.data_received()); |
| } |
| } else { |
| EXPECT_EQ(ERR_CONTENT_LENGTH_MISMATCH, d.request_status()) |
| << " Parameter = \"" << test_file << "\""; |
| } |
| } |
| } |
| |
| TEST_F(URLRequestTestHTTP, RedirectLoadTiming) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| GURL destination_url = http_test_server()->GetURL("/"); |
| GURL original_url = |
| http_test_server()->GetURL("/server-redirect?" + destination_url.spec()); |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req(default_context().CreateRequest( |
| original_url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->Start(); |
| d.RunUntilComplete(); |
| |
| EXPECT_EQ(1, d.response_started_count()); |
| EXPECT_EQ(1, d.received_redirect_count()); |
| EXPECT_EQ(destination_url, req->url()); |
| EXPECT_EQ(original_url, req->original_url()); |
| ASSERT_EQ(2U, req->url_chain().size()); |
| EXPECT_EQ(original_url, req->url_chain()[0]); |
| EXPECT_EQ(destination_url, req->url_chain()[1]); |
| |
| LoadTimingInfo load_timing_info_before_redirect; |
| EXPECT_TRUE(default_network_delegate().GetLoadTimingInfoBeforeRedirect( |
| &load_timing_info_before_redirect)); |
| TestLoadTimingNotReused(load_timing_info_before_redirect, |
| CONNECT_TIMING_HAS_DNS_TIMES); |
| |
| LoadTimingInfo load_timing_info; |
| req->GetLoadTimingInfo(&load_timing_info); |
| TestLoadTimingNotReused(load_timing_info, CONNECT_TIMING_HAS_DNS_TIMES); |
| |
| // Check that a new socket was used on redirect, since the server does not |
| // supposed keep-alive sockets, and that the times before the redirect are |
| // before the ones recorded for the second request. |
| EXPECT_NE(load_timing_info_before_redirect.socket_log_id, |
| load_timing_info.socket_log_id); |
| EXPECT_LE(load_timing_info_before_redirect.receive_headers_end, |
| load_timing_info.connect_timing.connect_start); |
| } |
| |
| TEST_F(URLRequestTestHTTP, MultipleRedirectTest) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| GURL destination_url = http_test_server()->GetURL("/"); |
| GURL middle_redirect_url = |
| http_test_server()->GetURL("/server-redirect?" + destination_url.spec()); |
| GURL original_url = http_test_server()->GetURL("/server-redirect?" + |
| middle_redirect_url.spec()); |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req(default_context().CreateRequest( |
| original_url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->Start(); |
| d.RunUntilComplete(); |
| |
| EXPECT_EQ(1, d.response_started_count()); |
| EXPECT_EQ(2, d.received_redirect_count()); |
| EXPECT_EQ(destination_url, req->url()); |
| EXPECT_EQ(original_url, req->original_url()); |
| ASSERT_EQ(3U, req->url_chain().size()); |
| EXPECT_EQ(original_url, req->url_chain()[0]); |
| EXPECT_EQ(middle_redirect_url, req->url_chain()[1]); |
| EXPECT_EQ(destination_url, req->url_chain()[2]); |
| } |
| |
| // This is a regression test for https://crbug.com/942073. |
| TEST_F(URLRequestTestHTTP, RedirectEscaping) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| // Assemble the destination URL as a string so it is not escaped by GURL. |
| GURL destination_base = http_test_server()->GetURL("/defaultresponse"); |
| // Add a URL fragment of U+2603 unescaped, U+2603 escaped, and then a UTF-8 |
| // encoding error. |
| std::string destination_url = |
| destination_base.spec() + "#\xE2\x98\x83_%E2%98%83_\xE0\xE0"; |
| // Redirect resolution should percent-escape bytes and preserve the UTF-8 |
| // error at the end. |
| std::string destination_escaped = |
| destination_base.spec() + "#%E2%98%83_%E2%98%83_%E0%E0"; |
| GURL original_url = http_test_server()->GetURL( |
| "/server-redirect?" + |
| base::EscapeQueryParamValue(destination_url, false)); |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req(default_context().CreateRequest( |
| original_url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->Start(); |
| d.RunUntilComplete(); |
| |
| EXPECT_EQ(1, d.response_started_count()); |
| EXPECT_EQ(1, d.received_redirect_count()); |
| EXPECT_EQ(destination_escaped, req->url().spec()); |
| EXPECT_EQ(original_url, req->original_url()); |
| ASSERT_EQ(2U, req->url_chain().size()); |
| EXPECT_EQ(original_url, req->url_chain()[0]); |
| EXPECT_EQ(destination_escaped, req->url_chain()[1].spec()); |
| } |
| |
| // First and second pieces of information logged by delegates to URLRequests. |
| const char kFirstDelegateInfo[] = "Wonderful delegate"; |
| const char16_t kFirstDelegateInfo16[] = u"Wonderful delegate"; |
| const char kSecondDelegateInfo[] = "Exciting delegate"; |
| const char16_t kSecondDelegateInfo16[] = u"Exciting delegate"; |
| |
| // Logs delegate information to a URLRequest. The first string is logged |
| // synchronously on Start(), using DELEGATE_INFO_DEBUG_ONLY. The second is |
| // logged asynchronously, using DELEGATE_INFO_DISPLAY_TO_USER. Then |
| // another asynchronous call is used to clear the delegate information |
| // before calling a callback. The object then deletes itself. |
| class AsyncDelegateLogger : public base::RefCounted<AsyncDelegateLogger> { |
| public: |
| using Callback = base::OnceCallback<void()>; |
| |
| AsyncDelegateLogger(const AsyncDelegateLogger&) = delete; |
| AsyncDelegateLogger& operator=(const AsyncDelegateLogger&) = delete; |
| |
| // Each time delegate information is added to the URLRequest, the resulting |
| // load state is checked. The expected load state after each request is |
| // passed in as an argument. |
| static void Run(URLRequest* url_request, |
| LoadState expected_first_load_state, |
| LoadState expected_second_load_state, |
| LoadState expected_third_load_state, |
| Callback callback) { |
| // base::MakeRefCounted<AsyncDelegateLogger> is unavailable here, since the |
| // constructor of AsyncDelegateLogger is private. |
| auto logger = base::WrapRefCounted(new AsyncDelegateLogger( |
| url_request, expected_first_load_state, expected_second_load_state, |
| expected_third_load_state, std::move(callback))); |
| logger->Start(); |
| } |
| |
| // Checks that the log entries, starting with log_position, contain the |
| // DELEGATE_INFO NetLog events that an AsyncDelegateLogger should have |
| // recorded. Returns the index of entry after the expected number of |
| // events this logged, or entries.size() if there aren't enough entries. |
| static size_t CheckDelegateInfo(const std::vector<NetLogEntry>& entries, |
| size_t log_position) { |
| // There should be 4 DELEGATE_INFO events: Two begins and two ends. |
| if (log_position + 3 >= entries.size()) { |
| ADD_FAILURE() << "Not enough log entries"; |
| return entries.size(); |
| } |
| std::string delegate_info; |
| EXPECT_EQ(NetLogEventType::DELEGATE_INFO, entries[log_position].type); |
| EXPECT_EQ(NetLogEventPhase::BEGIN, entries[log_position].phase); |
| EXPECT_EQ( |
| kFirstDelegateInfo, |
| GetStringValueFromParams(entries[log_position], "delegate_blocked_by")); |
| |
| ++log_position; |
| EXPECT_EQ(NetLogEventType::DELEGATE_INFO, entries[log_position].type); |
| EXPECT_EQ(NetLogEventPhase::END, entries[log_position].phase); |
| |
| ++log_position; |
| EXPECT_EQ(NetLogEventType::DELEGATE_INFO, entries[log_position].type); |
| EXPECT_EQ(NetLogEventPhase::BEGIN, entries[log_position].phase); |
| EXPECT_EQ( |
| kSecondDelegateInfo, |
| GetStringValueFromParams(entries[log_position], "delegate_blocked_by")); |
| |
| ++log_position; |
| EXPECT_EQ(NetLogEventType::DELEGATE_INFO, entries[log_position].type); |
| EXPECT_EQ(NetLogEventPhase::END, entries[log_position].phase); |
| |
| return log_position + 1; |
| } |
| |
| private: |
| friend class base::RefCounted<AsyncDelegateLogger>; |
| |
| AsyncDelegateLogger(URLRequest* url_request, |
| LoadState expected_first_load_state, |
| LoadState expected_second_load_state, |
| LoadState expected_third_load_state, |
| Callback callback) |
| : url_request_(url_request), |
| expected_first_load_state_(expected_first_load_state), |
| expected_second_load_state_(expected_second_load_state), |
| expected_third_load_state_(expected_third_load_state), |
| callback_(std::move(callback)) {} |
| |
| ~AsyncDelegateLogger() = default; |
| |
| void Start() { |
| url_request_->LogBlockedBy(kFirstDelegateInfo); |
| LoadStateWithParam load_state = url_request_->GetLoadState(); |
| EXPECT_EQ(expected_first_load_state_, load_state.state); |
| EXPECT_NE(kFirstDelegateInfo16, load_state.param); |
| base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask( |
| FROM_HERE, |
| base::BindOnce(&AsyncDelegateLogger::LogSecondDelegate, this)); |
| } |
| |
| void LogSecondDelegate() { |
| url_request_->LogAndReportBlockedBy(kSecondDelegateInfo); |
| LoadStateWithParam load_state = url_request_->GetLoadState(); |
| EXPECT_EQ(expected_second_load_state_, load_state.state); |
| if (expected_second_load_state_ == LOAD_STATE_WAITING_FOR_DELEGATE) { |
| EXPECT_EQ(kSecondDelegateInfo16, load_state.param); |
| } else { |
| EXPECT_NE(kSecondDelegateInfo16, load_state.param); |
| } |
| base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask( |
| FROM_HERE, base::BindOnce(&AsyncDelegateLogger::LogComplete, this)); |
| } |
| |
| void LogComplete() { |
| url_request_->LogUnblocked(); |
| LoadStateWithParam load_state = url_request_->GetLoadState(); |
| EXPECT_EQ(expected_third_load_state_, load_state.state); |
| if (expected_second_load_state_ == LOAD_STATE_WAITING_FOR_DELEGATE) |
| EXPECT_EQ(std::u16string(), load_state.param); |
| std::move(callback_).Run(); |
| } |
| |
| raw_ptr<URLRequest> url_request_; |
| const int expected_first_load_state_; |
| const int expected_second_load_state_; |
| const int expected_third_load_state_; |
| Callback callback_; |
| }; |
| |
| // NetworkDelegate that logs delegate information before a request is started, |
| // before headers are sent, when headers are read, and when auth information |
| // is requested. Uses AsyncDelegateLogger. |
| class AsyncLoggingNetworkDelegate : public TestNetworkDelegate { |
| public: |
| AsyncLoggingNetworkDelegate() = default; |
| |
| AsyncLoggingNetworkDelegate(const AsyncLoggingNetworkDelegate&) = delete; |
| AsyncLoggingNetworkDelegate& operator=(const AsyncLoggingNetworkDelegate&) = |
| delete; |
| |
| ~AsyncLoggingNetworkDelegate() override = default; |
| |
| // NetworkDelegate implementation. |
| int OnBeforeURLRequest(URLRequest* request, |
| CompletionOnceCallback callback, |
| GURL* new_url) override { |
| // TestNetworkDelegate always completes synchronously. |
| CHECK_NE(ERR_IO_PENDING, TestNetworkDelegate::OnBeforeURLRequest( |
| request, base::NullCallback(), new_url)); |
| return RunCallbackAsynchronously(request, std::move(callback)); |
| } |
| |
| int OnBeforeStartTransaction( |
| URLRequest* request, |
| const HttpRequestHeaders& headers, |
| OnBeforeStartTransactionCallback callback) override { |
| // TestNetworkDelegate always completes synchronously. |
| CHECK_NE(ERR_IO_PENDING, TestNetworkDelegate::OnBeforeStartTransaction( |
| request, headers, base::NullCallback())); |
| return RunCallbackAsynchronously( |
| request, base::BindOnce( |
| [](OnBeforeStartTransactionCallback callback, int result) { |
| std::move(callback).Run(result, absl::nullopt); |
| }, |
| std::move(callback))); |
| } |
| |
| int OnHeadersReceived( |
| URLRequest* request, |
| CompletionOnceCallback callback, |
| const HttpResponseHeaders* original_response_headers, |
| scoped_refptr<HttpResponseHeaders>* override_response_headers, |
| const IPEndPoint& endpoint, |
| absl::optional<GURL>* preserve_fragment_on_redirect_url) override { |
| // TestNetworkDelegate always completes synchronously. |
| CHECK_NE(ERR_IO_PENDING, |
| TestNetworkDelegate::OnHeadersReceived( |
| request, base::NullCallback(), original_response_headers, |
| override_response_headers, endpoint, |
| preserve_fragment_on_redirect_url)); |
| return RunCallbackAsynchronously(request, std::move(callback)); |
| } |
| |
| private: |
| static int RunCallbackAsynchronously(URLRequest* request, |
| CompletionOnceCallback callback) { |
| AsyncDelegateLogger::Run(request, LOAD_STATE_WAITING_FOR_DELEGATE, |
| LOAD_STATE_WAITING_FOR_DELEGATE, |
| LOAD_STATE_WAITING_FOR_DELEGATE, |
| base::BindOnce(std::move(callback), OK)); |
| return ERR_IO_PENDING; |
| } |
| }; |
| |
| // URLRequest::Delegate that logs delegate information when the headers |
| // are received, when each read completes, and during redirects. Uses |
| // AsyncDelegateLogger. Can optionally cancel a request in any phase. |
| // |
| // Inherits from TestDelegate to reuse the TestDelegate code to handle |
| // advancing to the next step in most cases, as well as cancellation. |
| class AsyncLoggingUrlRequestDelegate : public TestDelegate { |
| public: |
| enum CancelStage { |
| NO_CANCEL = 0, |
| CANCEL_ON_RECEIVED_REDIRECT, |
| CANCEL_ON_RESPONSE_STARTED, |
| CANCEL_ON_READ_COMPLETED |
| }; |
| |
| explicit AsyncLoggingUrlRequestDelegate(CancelStage cancel_stage) |
| : cancel_stage_(cancel_stage) { |
| if (cancel_stage == CANCEL_ON_RECEIVED_REDIRECT) |
| set_cancel_in_received_redirect(true); |
| else if (cancel_stage == CANCEL_ON_RESPONSE_STARTED) |
| set_cancel_in_response_started(true); |
| else if (cancel_stage == CANCEL_ON_READ_COMPLETED) |
| set_cancel_in_received_data(true); |
| } |
| |
| AsyncLoggingUrlRequestDelegate(const AsyncLoggingUrlRequestDelegate&) = |
| delete; |
| AsyncLoggingUrlRequestDelegate& operator=( |
| const AsyncLoggingUrlRequestDelegate&) = delete; |
| |
| ~AsyncLoggingUrlRequestDelegate() override = default; |
| |
| // URLRequest::Delegate implementation: |
| void OnReceivedRedirect(URLRequest* request, |
| const RedirectInfo& redirect_info, |
| bool* defer_redirect) override { |
| *defer_redirect = true; |
| AsyncDelegateLogger::Run( |
| request, LOAD_STATE_WAITING_FOR_DELEGATE, |
| LOAD_STATE_WAITING_FOR_DELEGATE, LOAD_STATE_WAITING_FOR_DELEGATE, |
| base::BindOnce( |
| &AsyncLoggingUrlRequestDelegate::OnReceivedRedirectLoggingComplete, |
| base::Unretained(this), request, redirect_info)); |
| } |
| |
| void OnResponseStarted(URLRequest* request, int net_error) override { |
| AsyncDelegateLogger::Run( |
| request, LOAD_STATE_WAITING_FOR_DELEGATE, |
| LOAD_STATE_WAITING_FOR_DELEGATE, LOAD_STATE_WAITING_FOR_DELEGATE, |
| base::BindOnce( |
| &AsyncLoggingUrlRequestDelegate::OnResponseStartedLoggingComplete, |
| base::Unretained(this), request, net_error)); |
| } |
| |
| void OnReadCompleted(URLRequest* request, int bytes_read) override { |
| AsyncDelegateLogger::Run( |
| request, LOAD_STATE_IDLE, LOAD_STATE_IDLE, LOAD_STATE_IDLE, |
| base::BindOnce( |
| &AsyncLoggingUrlRequestDelegate::AfterReadCompletedLoggingComplete, |
| base::Unretained(this), request, bytes_read)); |
| } |
| |
| private: |
| void OnReceivedRedirectLoggingComplete(URLRequest* request, |
| const RedirectInfo& redirect_info) { |
| bool defer_redirect = false; |
| TestDelegate::OnReceivedRedirect(request, redirect_info, &defer_redirect); |
| // FollowDeferredRedirect should not be called after cancellation. |
| if (cancel_stage_ == CANCEL_ON_RECEIVED_REDIRECT) |
| return; |
| if (!defer_redirect) { |
| request->FollowDeferredRedirect(absl::nullopt /* removed_headers */, |
| absl::nullopt /* modified_headers */); |
| } |
| } |
| |
| void OnResponseStartedLoggingComplete(URLRequest* request, int net_error) { |
| // The parent class continues the request. |
| TestDelegate::OnResponseStarted(request, net_error); |
| } |
| |
| void AfterReadCompletedLoggingComplete(URLRequest* request, int bytes_read) { |
| // The parent class continues the request. |
| TestDelegate::OnReadCompleted(request, bytes_read); |
| } |
| |
| const CancelStage cancel_stage_; |
| }; |
| |
| // Tests handling of delegate info before a request starts. |
| TEST_F(URLRequestTestHTTP, DelegateInfoBeforeStart) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| TestDelegate request_delegate; |
| auto context_builder = CreateTestURLRequestContextBuilder(); |
| context_builder->set_net_log(NetLog::Get()); |
| auto context = context_builder->Build(); |
| |
| { |
| std::unique_ptr<URLRequest> r(context->CreateRequest( |
| http_test_server()->GetURL("/defaultresponse"), DEFAULT_PRIORITY, |
| &request_delegate, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| LoadStateWithParam load_state = r->GetLoadState(); |
| EXPECT_EQ(LOAD_STATE_IDLE, load_state.state); |
| EXPECT_EQ(std::u16string(), load_state.param); |
| |
| AsyncDelegateLogger::Run( |
| r.get(), LOAD_STATE_WAITING_FOR_DELEGATE, |
| LOAD_STATE_WAITING_FOR_DELEGATE, LOAD_STATE_IDLE, |
| base::BindOnce(&URLRequest::Start, base::Unretained(r.get()))); |
| |
| request_delegate.RunUntilComplete(); |
| |
| EXPECT_EQ(200, r->GetResponseCode()); |
| EXPECT_EQ(OK, request_delegate.request_status()); |
| } |
| |
| auto entries = net_log_observer_.GetEntries(); |
| size_t log_position = ExpectLogContainsSomewhereAfter( |
| entries, 0, NetLogEventType::DELEGATE_INFO, NetLogEventPhase::BEGIN); |
| |
| log_position = AsyncDelegateLogger::CheckDelegateInfo(entries, log_position); |
| |
| // Nothing else should add any delegate info to the request. |
| EXPECT_FALSE(LogContainsEntryWithTypeAfter(entries, log_position + 1, |
| NetLogEventType::DELEGATE_INFO)); |
| } |
| |
| // Tests handling of delegate info from a network delegate. |
| TEST_F(URLRequestTestHTTP, NetworkDelegateInfo) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| TestDelegate request_delegate; |
| auto context_builder = CreateTestURLRequestContextBuilder(); |
| auto& network_delegate = *context_builder->set_network_delegate( |
| std::make_unique<AsyncLoggingNetworkDelegate>()); |
| context_builder->set_net_log(NetLog::Get()); |
| auto context = context_builder->Build(); |
| |
| { |
| std::unique_ptr<URLRequest> r(context->CreateRequest( |
| http_test_server()->GetURL("/simple.html"), DEFAULT_PRIORITY, |
| &request_delegate, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| LoadStateWithParam load_state = r->GetLoadState(); |
| EXPECT_EQ(LOAD_STATE_IDLE, load_state.state); |
| EXPECT_EQ(std::u16string(), load_state.param); |
| |
| r->Start(); |
| request_delegate.RunUntilComplete(); |
| |
| EXPECT_EQ(200, r->GetResponseCode()); |
| EXPECT_EQ(OK, request_delegate.request_status()); |
| EXPECT_EQ(1, network_delegate.created_requests()); |
| EXPECT_EQ(0, network_delegate.destroyed_requests()); |
| } |
| EXPECT_EQ(1, network_delegate.destroyed_requests()); |
| |
| size_t log_position = 0; |
| auto entries = net_log_observer_.GetEntries(); |
| static const NetLogEventType kExpectedEvents[] = { |
| NetLogEventType::NETWORK_DELEGATE_BEFORE_URL_REQUEST, |
| NetLogEventType::NETWORK_DELEGATE_BEFORE_START_TRANSACTION, |
| NetLogEventType::NETWORK_DELEGATE_HEADERS_RECEIVED, |
| }; |
| for (NetLogEventType event : kExpectedEvents) { |
| SCOPED_TRACE(NetLogEventTypeToString(event)); |
| log_position = ExpectLogContainsSomewhereAfter( |
| entries, log_position + 1, event, NetLogEventPhase::BEGIN); |
| |
| log_position = |
| AsyncDelegateLogger::CheckDelegateInfo(entries, log_position + 1); |
| |
| ASSERT_LT(log_position, entries.size()); |
| EXPECT_EQ(event, entries[log_position].type); |
| EXPECT_EQ(NetLogEventPhase::END, entries[log_position].phase); |
| } |
| |
| EXPECT_FALSE(LogContainsEntryWithTypeAfter(entries, log_position + 1, |
| NetLogEventType::DELEGATE_INFO)); |
| } |
| |
| // Tests handling of delegate info from a network delegate in the case of an |
| // HTTP redirect. |
| TEST_F(URLRequestTestHTTP, NetworkDelegateInfoRedirect) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| TestDelegate request_delegate; |
| auto context_builder = CreateTestURLRequestContextBuilder(); |
| auto& network_delegate = *context_builder->set_network_delegate( |
| std::make_unique<AsyncLoggingNetworkDelegate>()); |
| auto context = context_builder->Build(); |
| |
| { |
| std::unique_ptr<URLRequest> r(context->CreateRequest( |
| http_test_server()->GetURL("/server-redirect?simple.html"), |
| DEFAULT_PRIORITY, &request_delegate, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| LoadStateWithParam load_state = r->GetLoadState(); |
| EXPECT_EQ(LOAD_STATE_IDLE, load_state.state); |
| EXPECT_EQ(std::u16string(), load_state.param); |
| |
| r->Start(); |
| request_delegate.RunUntilComplete(); |
| |
| EXPECT_EQ(200, r->GetResponseCode()); |
| EXPECT_EQ(OK, request_delegate.request_status()); |
| EXPECT_EQ(2, network_delegate.created_requests()); |
| EXPECT_EQ(0, network_delegate.destroyed_requests()); |
| } |
| EXPECT_EQ(1, network_delegate.destroyed_requests()); |
| |
| size_t log_position = 0; |
| auto entries = net_log_observer_.GetEntries(); |
| static const NetLogEventType kExpectedEvents[] = { |
| NetLogEventType::NETWORK_DELEGATE_BEFORE_URL_REQUEST, |
| NetLogEventType::NETWORK_DELEGATE_BEFORE_START_TRANSACTION, |
| NetLogEventType::NETWORK_DELEGATE_HEADERS_RECEIVED, |
| }; |
| for (NetLogEventType event : kExpectedEvents) { |
| SCOPED_TRACE(NetLogEventTypeToString(event)); |
| log_position = ExpectLogContainsSomewhereAfter( |
| entries, log_position + 1, event, NetLogEventPhase::BEGIN); |
| |
| log_position = |
| AsyncDelegateLogger::CheckDelegateInfo(entries, log_position + 1); |
| |
| ASSERT_LT(log_position, entries.size()); |
| EXPECT_EQ(event, entries[log_position].type); |
| EXPECT_EQ(NetLogEventPhase::END, entries[log_position].phase); |
| } |
| |
| // The URLRequest::Delegate then gets informed about the redirect. |
| log_position = ExpectLogContainsSomewhereAfter( |
| entries, log_position + 1, |
| NetLogEventType::URL_REQUEST_DELEGATE_RECEIVED_REDIRECT, |
| NetLogEventPhase::BEGIN); |
| |
| // The NetworkDelegate logged information in the same three events as before. |
| for (NetLogEventType event : kExpectedEvents) { |
| SCOPED_TRACE(NetLogEventTypeToString(event)); |
| log_position = ExpectLogContainsSomewhereAfter( |
| entries, log_position + 1, event, NetLogEventPhase::BEGIN); |
| |
| log_position = |
| AsyncDelegateLogger::CheckDelegateInfo(entries, log_position + 1); |
| |
| ASSERT_LT(log_position, entries.size()); |
| EXPECT_EQ(event, entries[log_position].type); |
| EXPECT_EQ(NetLogEventPhase::END, entries[log_position].phase); |
| } |
| |
| EXPECT_FALSE(LogContainsEntryWithTypeAfter(entries, log_position + 1, |
| NetLogEventType::DELEGATE_INFO)); |
| } |
| |
| // Tests handling of delegate info from a URLRequest::Delegate. |
| TEST_F(URLRequestTestHTTP, URLRequestDelegateInfo) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| AsyncLoggingUrlRequestDelegate request_delegate( |
| AsyncLoggingUrlRequestDelegate::NO_CANCEL); |
| auto context_builder = CreateTestURLRequestContextBuilder(); |
| context_builder->set_net_log(NetLog::Get()); |
| auto context = context_builder->Build(); |
| |
| { |
| // A chunked response with delays between chunks is used to make sure that |
| // attempts by the URLRequest delegate to log information while reading the |
| // body are ignored. Since they are ignored, this test is robust against |
| // the possibility of multiple reads being combined in the unlikely event |
| // that it occurs. |
| std::unique_ptr<URLRequest> r(context->CreateRequest( |
| http_test_server()->GetURL("/chunked?waitBetweenChunks=20"), |
| DEFAULT_PRIORITY, &request_delegate, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| LoadStateWithParam load_state = r->GetLoadState(); |
| r->Start(); |
| request_delegate.RunUntilComplete(); |
| |
| EXPECT_EQ(200, r->GetResponseCode()); |
| EXPECT_EQ(OK, request_delegate.request_status()); |
| } |
| |
| auto entries = net_log_observer_.GetEntries(); |
| |
| size_t log_position = 0; |
| |
| // The delegate info should only have been logged on header complete. Other |
| // times it should silently be ignored. |
| log_position = ExpectLogContainsSomewhereAfter( |
| entries, log_position + 1, |
| NetLogEventType::URL_REQUEST_DELEGATE_RESPONSE_STARTED, |
| NetLogEventPhase::BEGIN); |
| |
| log_position = |
| AsyncDelegateLogger::CheckDelegateInfo(entries, log_position + 1); |
| |
| ASSERT_LT(log_position, entries.size()); |
| EXPECT_EQ(NetLogEventType::URL_REQUEST_DELEGATE_RESPONSE_STARTED, |
| entries[log_position].type); |
| EXPECT_EQ(NetLogEventPhase::END, entries[log_position].phase); |
| |
| EXPECT_FALSE(LogContainsEntryWithTypeAfter(entries, log_position + 1, |
| NetLogEventType::DELEGATE_INFO)); |
| EXPECT_FALSE(LogContainsEntryWithTypeAfter( |
| entries, log_position + 1, |
| NetLogEventType::URL_REQUEST_DELEGATE_RESPONSE_STARTED)); |
| } |
| |
| // Tests handling of delegate info from a URLRequest::Delegate in the case of |
| // an HTTP redirect. |
| TEST_F(URLRequestTestHTTP, URLRequestDelegateInfoOnRedirect) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| AsyncLoggingUrlRequestDelegate request_delegate( |
| AsyncLoggingUrlRequestDelegate::NO_CANCEL); |
| auto context_builder = CreateTestURLRequestContextBuilder(); |
| context_builder->set_net_log(NetLog::Get()); |
| auto context = context_builder->Build(); |
| |
| { |
| std::unique_ptr<URLRequest> r(context->CreateRequest( |
| http_test_server()->GetURL("/server-redirect?simple.html"), |
| DEFAULT_PRIORITY, &request_delegate, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| LoadStateWithParam load_state = r->GetLoadState(); |
| r->Start(); |
| request_delegate.RunUntilComplete(); |
| |
| EXPECT_EQ(200, r->GetResponseCode()); |
| EXPECT_EQ(OK, request_delegate.request_status()); |
| } |
| |
| auto entries = net_log_observer_.GetEntries(); |
| |
| // Delegate info should only have been logged in OnReceivedRedirect and |
| // OnResponseStarted. |
| size_t log_position = 0; |
| static const NetLogEventType kExpectedEvents[] = { |
| NetLogEventType::URL_REQUEST_DELEGATE_RECEIVED_REDIRECT, |
| NetLogEventType::URL_REQUEST_DELEGATE_RESPONSE_STARTED, |
| }; |
| for (NetLogEventType event : kExpectedEvents) { |
| SCOPED_TRACE(NetLogEventTypeToString(event)); |
| log_position = ExpectLogContainsSomewhereAfter(entries, log_position, event, |
| NetLogEventPhase::BEGIN); |
| |
| log_position = |
| AsyncDelegateLogger::CheckDelegateInfo(entries, log_position + 1); |
| |
| ASSERT_LT(log_position, entries.size()); |
| EXPECT_EQ(event, entries[log_position].type); |
| EXPECT_EQ(NetLogEventPhase::END, entries[log_position].phase); |
| } |
| |
| EXPECT_FALSE(LogContainsEntryWithTypeAfter(entries, log_position + 1, |
| NetLogEventType::DELEGATE_INFO)); |
| } |
| |
| // Tests handling of delegate info from a URLRequest::Delegate in the case of |
| // an HTTP redirect, with cancellation at various points. |
| TEST_F(URLRequestTestHTTP, URLRequestDelegateOnRedirectCancelled) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| const AsyncLoggingUrlRequestDelegate::CancelStage kCancelStages[] = { |
| AsyncLoggingUrlRequestDelegate::CANCEL_ON_RECEIVED_REDIRECT, |
| AsyncLoggingUrlRequestDelegate::CANCEL_ON_RESPONSE_STARTED, |
| AsyncLoggingUrlRequestDelegate::CANCEL_ON_READ_COMPLETED, |
| }; |
| |
| for (auto cancel_stage : kCancelStages) { |
| AsyncLoggingUrlRequestDelegate request_delegate(cancel_stage); |
| RecordingNetLogObserver net_log_observer; |
| auto context_builder = CreateTestURLRequestContextBuilder(); |
| context_builder->set_net_log(NetLog::Get()); |
| auto context = context_builder->Build(); |
| |
| { |
| std::unique_ptr<URLRequest> r(context->CreateRequest( |
| http_test_server()->GetURL("/server-redirect?simple.html"), |
| DEFAULT_PRIORITY, &request_delegate, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| LoadStateWithParam load_state = r->GetLoadState(); |
| r->Start(); |
| request_delegate.RunUntilComplete(); |
| EXPECT_EQ(ERR_ABORTED, request_delegate.request_status()); |
| |
| // Spin the message loop to run AsyncDelegateLogger task(s) posted after |
| // the |request_delegate| completion task. |
| base::RunLoop().RunUntilIdle(); |
| } |
| |
| auto entries = net_log_observer.GetEntries(); |
| |
| // Delegate info is always logged in both OnReceivedRedirect and |
| // OnResponseStarted. In the CANCEL_ON_RECEIVED_REDIRECT, the |
| // OnResponseStarted delegate call is after cancellation, but logging is |
| // still currently supported in that call. |
| size_t log_position = 0; |
| static const NetLogEventType kExpectedEvents[] = { |
| NetLogEventType::URL_REQUEST_DELEGATE_RECEIVED_REDIRECT, |
| NetLogEventType::URL_REQUEST_DELEGATE_RESPONSE_STARTED, |
| }; |
| for (NetLogEventType event : kExpectedEvents) { |
| SCOPED_TRACE(NetLogEventTypeToString(event)); |
| log_position = ExpectLogContainsSomewhereAfter( |
| entries, log_position, event, NetLogEventPhase::BEGIN); |
| |
| log_position = |
| AsyncDelegateLogger::CheckDelegateInfo(entries, log_position + 1); |
| |
| ASSERT_LT(log_position, entries.size()); |
| EXPECT_EQ(event, entries[log_position].type); |
| EXPECT_EQ(NetLogEventPhase::END, entries[log_position].phase); |
| } |
| |
| EXPECT_FALSE(LogContainsEntryWithTypeAfter(entries, log_position + 1, |
| NetLogEventType::DELEGATE_INFO)); |
| } |
| } |
| |
| namespace { |
| |
| const char kExtraHeader[] = "Allow-Snafu"; |
| const char kExtraValue[] = "fubar"; |
| |
| class RedirectWithAdditionalHeadersDelegate : public TestDelegate { |
| void OnReceivedRedirect(URLRequest* request, |
| const RedirectInfo& redirect_info, |
| bool* defer_redirect) override { |
| TestDelegate::OnReceivedRedirect(request, redirect_info, defer_redirect); |
| request->SetExtraRequestHeaderByName(kExtraHeader, kExtraValue, false); |
| } |
| }; |
| |
| } // namespace |
| |
| TEST_F(URLRequestTestHTTP, RedirectWithAdditionalHeadersTest) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| GURL destination_url = |
| http_test_server()->GetURL("/echoheader?" + std::string(kExtraHeader)); |
| GURL original_url = |
| http_test_server()->GetURL("/server-redirect?" + destination_url.spec()); |
| RedirectWithAdditionalHeadersDelegate d; |
| std::unique_ptr<URLRequest> req(default_context().CreateRequest( |
| original_url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->Start(); |
| d.RunUntilComplete(); |
| |
| std::string value; |
| const HttpRequestHeaders& headers = req->extra_request_headers(); |
| EXPECT_TRUE(headers.GetHeader(kExtraHeader, &value)); |
| EXPECT_EQ(kExtraValue, value); |
| EXPECT_FALSE(req->is_pending()); |
| EXPECT_FALSE(req->is_redirecting()); |
| EXPECT_EQ(kExtraValue, d.data_received()); |
| } |
| |
| namespace { |
| |
| const char kExtraHeaderToRemove[] = "To-Be-Removed"; |
| |
| class RedirectWithHeaderRemovalDelegate : public TestDelegate { |
| void OnReceivedRedirect(URLRequest* request, |
| const RedirectInfo& redirect_info, |
| bool* defer_redirect) override { |
| TestDelegate::OnReceivedRedirect(request, redirect_info, defer_redirect); |
| request->RemoveRequestHeaderByName(kExtraHeaderToRemove); |
| } |
| }; |
| |
| } // namespace |
| |
| TEST_F(URLRequestTestHTTP, RedirectWithHeaderRemovalTest) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| GURL destination_url = http_test_server()->GetURL( |
| "/echoheader?" + std::string(kExtraHeaderToRemove)); |
| GURL original_url = |
| http_test_server()->GetURL("/server-redirect?" + destination_url.spec()); |
| RedirectWithHeaderRemovalDelegate d; |
| std::unique_ptr<URLRequest> req(default_context().CreateRequest( |
| original_url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->SetExtraRequestHeaderByName(kExtraHeaderToRemove, "dummy", false); |
| req->Start(); |
| d.RunUntilComplete(); |
| |
| std::string value; |
| const HttpRequestHeaders& headers = req->extra_request_headers(); |
| EXPECT_FALSE(headers.GetHeader(kExtraHeaderToRemove, &value)); |
| EXPECT_FALSE(req->is_pending()); |
| EXPECT_FALSE(req->is_redirecting()); |
| EXPECT_EQ("None", d.data_received()); |
| } |
| |
| TEST_F(URLRequestTestHTTP, CancelAfterStart) { |
| TestDelegate d; |
| { |
| std::unique_ptr<URLRequest> r(default_context().CreateRequest( |
| GURL("http://www.google.com/"), DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| |
| r->Start(); |
| EXPECT_TRUE(r->is_pending()); |
| |
| r->Cancel(); |
| |
| d.RunUntilComplete(); |
| |
| // We expect to receive OnResponseStarted even though the request has been |
| // cancelled. |
| EXPECT_EQ(1, d.response_started_count()); |
| EXPECT_EQ(0, d.bytes_received()); |
| EXPECT_FALSE(d.received_data_before_response()); |
| } |
| } |
| |
| TEST_F(URLRequestTestHTTP, CancelInResponseStarted) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| TestDelegate d; |
| { |
| std::unique_ptr<URLRequest> r(default_context().CreateRequest( |
| http_test_server()->GetURL("/"), DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| |
| d.set_cancel_in_response_started(true); |
| |
| r->Start(); |
| EXPECT_TRUE(r->is_pending()); |
| |
| d.RunUntilComplete(); |
| |
| EXPECT_EQ(1, d.response_started_count()); |
| EXPECT_EQ(0, d.bytes_received()); |
| EXPECT_FALSE(d.received_data_before_response()); |
| EXPECT_EQ(ERR_ABORTED, d.request_status()); |
| } |
| } |
| |
| TEST_F(URLRequestTestHTTP, CancelOnDataReceived) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| TestDelegate d; |
| { |
| std::unique_ptr<URLRequest> r(default_context().CreateRequest( |
| http_test_server()->GetURL("/defaultresponse"), DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| |
| d.set_cancel_in_received_data(true); |
| |
| r->Start(); |
| EXPECT_TRUE(r->is_pending()); |
| |
| d.RunUntilComplete(); |
| |
| EXPECT_EQ(1, d.response_started_count()); |
| EXPECT_NE(0, d.received_bytes_count()); |
| EXPECT_FALSE(d.received_data_before_response()); |
| EXPECT_EQ(ERR_ABORTED, d.request_status()); |
| } |
| } |
| |
| TEST_F(URLRequestTestHTTP, CancelDuringEofRead) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| TestDelegate d; |
| { |
| // This returns an empty response (With headers). |
| std::unique_ptr<URLRequest> r(default_context().CreateRequest( |
| http_test_server()->GetURL("/"), DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| |
| d.set_cancel_in_received_data(true); |
| |
| r->Start(); |
| EXPECT_TRUE(r->is_pending()); |
| |
| d.RunUntilComplete(); |
| |
| EXPECT_EQ(1, d.response_started_count()); |
| EXPECT_EQ(0, d.received_bytes_count()); |
| EXPECT_FALSE(d.received_data_before_response()); |
| EXPECT_EQ(ERR_ABORTED, d.request_status()); |
| } |
| } |
| |
| TEST_F(URLRequestTestHTTP, CancelByDestroyingAfterStart) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| TestDelegate d; |
| { |
| std::unique_ptr<URLRequest> r(default_context().CreateRequest( |
| http_test_server()->GetURL("/"), DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| |
| r->Start(); |
| EXPECT_TRUE(r->is_pending()); |
| |
| // The request will be implicitly canceled when it is destroyed. The |
| // test delegate must not post a quit message when this happens because |
| // this test doesn't actually have a message loop. The quit message would |
| // get put on this thread's message queue and the next test would exit |
| // early, causing problems. |
| d.set_on_complete(base::DoNothing()); |
| } |
| // expect things to just cleanup properly. |
| |
| // we won't actually get a received response here because we've never run the |
| // message loop |
| EXPECT_FALSE(d.received_data_before_response()); |
| EXPECT_EQ(0, d.bytes_received()); |
| } |
| |
| TEST_F(URLRequestTestHTTP, CancelWhileReadingFromCache) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| // populate cache |
| { |
| TestDelegate d; |
| std::unique_ptr<URLRequest> r(default_context().CreateRequest( |
| http_test_server()->GetURL("/cachetime"), DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| r->Start(); |
| d.RunUntilComplete(); |
| EXPECT_EQ(OK, d.request_status()); |
| } |
| |
| // cancel read from cache (see bug 990242) |
| { |
| TestDelegate d; |
| std::unique_ptr<URLRequest> r(default_context().CreateRequest( |
| http_test_server()->GetURL("/cachetime"), DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| r->Start(); |
| r->Cancel(); |
| d.RunUntilComplete(); |
| |
| EXPECT_EQ(ERR_ABORTED, d.request_status()); |
| EXPECT_EQ(1, d.response_started_count()); |
| EXPECT_EQ(0, d.bytes_received()); |
| EXPECT_FALSE(d.received_data_before_response()); |
| } |
| } |
| |
| TEST_F(URLRequestTestHTTP, PostTest) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| HTTPUploadDataOperationTest("POST"); |
| } |
| |
| TEST_F(URLRequestTestHTTP, PutTest) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| HTTPUploadDataOperationTest("PUT"); |
| } |
| |
| TEST_F(URLRequestTestHTTP, PostEmptyTest) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| TestDelegate d; |
| { |
| std::unique_ptr<URLRequest> r(default_context().CreateRequest( |
| http_test_server()->GetURL("/echo"), DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| r->set_method("POST"); |
| |
| r->Start(); |
| EXPECT_TRUE(r->is_pending()); |
| |
| d.RunUntilComplete(); |
| |
| ASSERT_EQ(1, d.response_started_count()) |
| << "request failed. Error: " << d.request_status(); |
| |
| EXPECT_FALSE(d.received_data_before_response()); |
| EXPECT_TRUE(d.data_received().empty()); |
| } |
| } |
| |
| TEST_F(URLRequestTestHTTP, PostFileTest) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| TestDelegate d; |
| { |
| std::unique_ptr<URLRequest> r(default_context().CreateRequest( |
| http_test_server()->GetURL("/echo"), DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| r->set_method("POST"); |
| |
| base::FilePath dir; |
| base::PathService::Get(base::DIR_EXE, &dir); |
| base::SetCurrentDirectory(dir); |
| |
| std::vector<std::unique_ptr<UploadElementReader>> element_readers; |
| |
| base::FilePath path; |
| base::PathService::Get(base::DIR_SOURCE_ROOT, &path); |
| path = path.Append(kTestFilePath); |
| path = path.Append(FILE_PATH_LITERAL("with-headers.html")); |
| element_readers.push_back(std::make_unique<UploadFileElementReader>( |
| base::SingleThreadTaskRunner::GetCurrentDefault().get(), path, 0, |
| std::numeric_limits<uint64_t>::max(), base::Time())); |
| r->set_upload(std::make_unique<ElementsUploadDataStream>( |
| std::move(element_readers), 0)); |
| |
| r->Start(); |
| EXPECT_TRUE(r->is_pending()); |
| |
| d.RunUntilComplete(); |
| |
| int64_t size64 = 0; |
| ASSERT_EQ(true, base::GetFileSize(path, &size64)); |
| ASSERT_LE(size64, std::numeric_limits<int>::max()); |
| int size = static_cast<int>(size64); |
| auto buf = std::make_unique<char[]>(size); |
| |
| ASSERT_EQ(size, base::ReadFile(path, buf.get(), size)); |
| |
| ASSERT_EQ(1, d.response_started_count()) |
| << "request failed. Error: " << d.request_status(); |
| |
| EXPECT_FALSE(d.received_data_before_response()); |
| |
| EXPECT_EQ(size, d.bytes_received()); |
| EXPECT_EQ(std::string(&buf[0], size), d.data_received()); |
| } |
| } |
| |
| TEST_F(URLRequestTestHTTP, PostUnreadableFileTest) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| TestDelegate d; |
| { |
| std::unique_ptr<URLRequest> r(default_context().CreateRequest( |
| http_test_server()->GetURL("/echo"), DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| r->set_method("POST"); |
| |
| std::vector<std::unique_ptr<UploadElementReader>> element_readers; |
| |
| element_readers.push_back(std::make_unique<UploadFileElementReader>( |
| base::SingleThreadTaskRunner::GetCurrentDefault().get(), |
| base::FilePath(FILE_PATH_LITERAL( |
| "c:\\path\\to\\non\\existant\\file.randomness.12345")), |
| 0, std::numeric_limits<uint64_t>::max(), base::Time())); |
| r->set_upload(std::make_unique<ElementsUploadDataStream>( |
| std::move(element_readers), 0)); |
| |
| r->Start(); |
| EXPECT_TRUE(r->is_pending()); |
| |
| d.RunUntilComplete(); |
| |
| EXPECT_TRUE(d.request_failed()); |
| EXPECT_FALSE(d.received_data_before_response()); |
| EXPECT_EQ(0, d.bytes_received()); |
| EXPECT_EQ(ERR_FILE_NOT_FOUND, d.request_status()); |
| } |
| } |
| |
| namespace { |
| |
| // Adds a standard set of data to an upload for chunked upload integration |
| // tests. |
| void AddDataToUpload(ChunkedUploadDataStream::Writer* writer) { |
| writer->AppendData("a", 1, false); |
| writer->AppendData("bcd", 3, false); |
| writer->AppendData("this is a longer chunk than before.", 35, false); |
| writer->AppendData("\r\n\r\n", 4, false); |
| writer->AppendData("0", 1, false); |
| writer->AppendData("2323", 4, true); |
| } |
| |
| // Checks that the upload data added in AddChunksToUpload() was echoed back from |
| // the server. |
| void VerifyReceivedDataMatchesChunks(URLRequest* r, TestDelegate* d) { |
| // This should match the chunks sent by AddChunksToUpload(). |
| const std::string expected_data = |
| "abcdthis is a longer chunk than before.\r\n\r\n02323"; |
| |
| ASSERT_EQ(1, d->response_started_count()) |
| << "request failed. Error: " << d->request_status(); |
| |
| EXPECT_FALSE(d->received_data_before_response()); |
| |
| EXPECT_EQ(expected_data.size(), static_cast<size_t>(d->bytes_received())); |
| EXPECT_EQ(expected_data, d->data_received()); |
| } |
| |
| } // namespace |
| |
| TEST_F(URLRequestTestHTTP, TestPostChunkedDataBeforeStart) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| TestDelegate d; |
| { |
| std::unique_ptr<URLRequest> r(default_context().CreateRequest( |
| http_test_server()->GetURL("/echo"), DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| auto upload_data_stream = std::make_unique<ChunkedUploadDataStream>(0); |
| std::unique_ptr<ChunkedUploadDataStream::Writer> writer = |
| upload_data_stream->CreateWriter(); |
| r->set_upload(std::move(upload_data_stream)); |
| r->set_method("POST"); |
| AddDataToUpload(writer.get()); |
| r->Start(); |
| EXPECT_TRUE(r->is_pending()); |
| |
| d.RunUntilComplete(); |
| |
| VerifyReceivedDataMatchesChunks(r.get(), &d); |
| } |
| } |
| |
| TEST_F(URLRequestTestHTTP, TestPostChunkedDataJustAfterStart) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| TestDelegate d; |
| { |
| std::unique_ptr<URLRequest> r(default_context().CreateRequest( |
| http_test_server()->GetURL("/echo"), DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| auto upload_data_stream = std::make_unique<ChunkedUploadDataStream>(0); |
| std::unique_ptr<ChunkedUploadDataStream::Writer> writer = |
| upload_data_stream->CreateWriter(); |
| r->set_upload(std::move(upload_data_stream)); |
| r->set_method("POST"); |
| r->Start(); |
| EXPECT_TRUE(r->is_pending()); |
| AddDataToUpload(writer.get()); |
| d.RunUntilComplete(); |
| |
| VerifyReceivedDataMatchesChunks(r.get(), &d); |
| } |
| } |
| |
| TEST_F(URLRequestTestHTTP, TestPostChunkedDataAfterStart) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| TestDelegate d; |
| { |
| std::unique_ptr<URLRequest> r(default_context().CreateRequest( |
| http_test_server()->GetURL("/echo"), DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| auto upload_data_stream = std::make_unique<ChunkedUploadDataStream>(0); |
| std::unique_ptr<ChunkedUploadDataStream::Writer> writer = |
| upload_data_stream->CreateWriter(); |
| r->set_upload(std::move(upload_data_stream)); |
| r->set_method("POST"); |
| r->Start(); |
| EXPECT_TRUE(r->is_pending()); |
| |
| // Pump messages until we start sending headers.. |
| base::RunLoop().RunUntilIdle(); |
| |
| // And now wait for completion. |
| base::RunLoop run_loop; |
| d.set_on_complete(run_loop.QuitClosure()); |
| AddDataToUpload(writer.get()); |
| run_loop.Run(); |
| |
| VerifyReceivedDataMatchesChunks(r.get(), &d); |
| } |
| } |
| |
| TEST_F(URLRequestTestHTTP, ResponseHeadersTest) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req(default_context().CreateRequest( |
| http_test_server()->GetURL("/with-headers.html"), DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->Start(); |
| d.RunUntilComplete(); |
| |
| const HttpResponseHeaders* headers = req->response_headers(); |
| |
| // Simple sanity check that response_info() accesses the same data. |
| EXPECT_EQ(headers, req->response_info().headers.get()); |
| |
| std::string header; |
| EXPECT_TRUE(headers->GetNormalizedHeader("cache-control", &header)); |
| EXPECT_EQ("private", header); |
| |
| header.clear(); |
| EXPECT_TRUE(headers->GetNormalizedHeader("content-type", &header)); |
| EXPECT_EQ("text/html; charset=ISO-8859-1", header); |
| |
| // The response has two "X-Multiple-Entries" headers. |
| // This verfies our output has them concatenated together. |
| header.clear(); |
| EXPECT_TRUE(headers->GetNormalizedHeader("x-multiple-entries", &header)); |
| EXPECT_EQ("a, b", header); |
| } |
| |
| // TODO(svaldez): iOS tests are flaky with EmbeddedTestServer and transport |
| // security state. (see http://crbug.com/550977). |
| #if !BUILDFLAG(IS_IOS) |
| TEST_F(URLRequestTestHTTP, ProcessSTS) { |
| EmbeddedTestServer https_test_server(net::EmbeddedTestServer::TYPE_HTTPS); |
| https_test_server.SetSSLConfig( |
| net::EmbeddedTestServer::CERT_COMMON_NAME_IS_DOMAIN); |
| https_test_server.ServeFilesFromSourceDirectory( |
| base::FilePath(kTestFilePath)); |
| ASSERT_TRUE(https_test_server.Start()); |
| |
| std::string test_server_hostname = https_test_server.GetURL("/").host(); |
| TestDelegate d; |
| std::unique_ptr<URLRequest> request(default_context().CreateRequest( |
| https_test_server.GetURL("/hsts-headers.html"), DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| request->Start(); |
| d.RunUntilComplete(); |
| |
| TransportSecurityState* security_state = |
| default_context().transport_security_state(); |
| TransportSecurityState::STSState sts_state; |
| TransportSecurityState::PKPState pkp_state; |
| EXPECT_TRUE( |
| security_state->GetDynamicSTSState(test_server_hostname, &sts_state)); |
| EXPECT_FALSE( |
| security_state->GetDynamicPKPState(test_server_hostname, &pkp_state)); |
| EXPECT_EQ(TransportSecurityState::STSState::MODE_FORCE_HTTPS, |
| sts_state.upgrade_mode); |
| EXPECT_TRUE(sts_state.include_subdomains); |
| EXPECT_FALSE(pkp_state.include_subdomains); |
| #if BUILDFLAG(IS_ANDROID) |
| // Android's CertVerifyProc does not (yet) handle pins. |
| #else |
| EXPECT_FALSE(pkp_state.HasPublicKeyPins()); |
| #endif |
| } |
| |
| TEST_F(URLRequestTestHTTP, STSNotProcessedOnIP) { |
| EmbeddedTestServer https_test_server(net::EmbeddedTestServer::TYPE_HTTPS); |
| https_test_server.ServeFilesFromSourceDirectory( |
| base::FilePath(kTestFilePath)); |
| ASSERT_TRUE(https_test_server.Start()); |
| // Make sure this test fails if the test server is changed to not |
| // listen on an IP by default. |
| ASSERT_TRUE(https_test_server.GetURL("/").HostIsIPAddress()); |
| std::string test_server_hostname = https_test_server.GetURL("/").host(); |
| |
| TestDelegate d; |
| std::unique_ptr<URLRequest> request(default_context().CreateRequest( |
| https_test_server.GetURL("/hsts-headers.html"), DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| request->Start(); |
| d.RunUntilComplete(); |
| TransportSecurityState* security_state = |
| default_context().transport_security_state(); |
| TransportSecurityState::STSState sts_state; |
| EXPECT_FALSE( |
| security_state->GetDynamicSTSState(test_server_hostname, &sts_state)); |
| } |
| |
| namespace { |
| const char kPKPReportUri[] = "http://report-uri.preloaded.test/pkp"; |
| const char kPKPHost[] = "with-report-uri-pkp.preloaded.test"; |
| } // namespace |
| |
| // Tests that reports get sent on PKP violations when a report-uri is set. |
| TEST_F(URLRequestTestHTTP, ProcessPKPAndSendReport) { |
| base::test::ScopedFeatureList scoped_feature_list_; |
| scoped_feature_list_.InitAndEnableFeature( |
| net::features::kStaticKeyPinningEnforcement); |
| GURL report_uri(kPKPReportUri); |
| EmbeddedTestServer https_test_server(net::EmbeddedTestServer::TYPE_HTTPS); |
| https_test_server.SetSSLConfig( |
| net::EmbeddedTestServer::CERT_COMMON_NAME_IS_DOMAIN); |
| https_test_server.ServeFilesFromSourceDirectory( |
| base::FilePath(kTestFilePath)); |
| ASSERT_TRUE(https_test_server.Start()); |
| |
| std::string test_server_hostname = kPKPHost; |
| |
| SetTransportSecurityStateSourceForTesting(&test_default::kHSTSSource); |
| |
| // Set up a MockCertVerifier to trigger a violation of the previously |
| // set pin. |
| scoped_refptr<X509Certificate> cert = https_test_server.GetCertificate(); |
| ASSERT_TRUE(cert); |
| |
| CertVerifyResult verify_result; |
| verify_result.verified_cert = cert; |
| verify_result.is_issued_by_known_root = true; |
| HashValue hash3; |
| ASSERT_TRUE( |
| hash3.FromString("sha256/3333333333333333333333333333333333333333333=")); |
| verify_result.public_key_hashes.push_back(hash3); |
| auto cert_verifier = std::make_unique<MockCertVerifier>(); |
| cert_verifier->AddResultForCert(cert.get(), verify_result, OK); |
| |
| MockCertificateReportSender mock_report_sender; // Must outlive `context`. |
| auto context_builder = CreateTestURLRequestContextBuilder(); |
| context_builder->SetCertVerifier(std::move(cert_verifier)); |
| auto context = context_builder->Build(); |
| context->transport_security_state()->EnableStaticPinsForTesting(); |
| context->transport_security_state()->SetPinningListAlwaysTimelyForTesting( |
| true); |
| context->transport_security_state()->SetReportSender(&mock_report_sender); |
| |
| IsolationInfo isolation_info = IsolationInfo::CreateTransient(); |
| |
| // Now send a request to trigger the violation. |
| TestDelegate d; |
| std::unique_ptr<URLRequest> violating_request(context->CreateRequest( |
| https_test_server.GetURL(test_server_hostname, "/simple.html"), |
| DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| violating_request->set_isolation_info(isolation_info); |
| violating_request->Start(); |
| d.RunUntilComplete(); |
| |
| // Check that a report was sent. |
| EXPECT_EQ(report_uri, mock_report_sender.latest_report_uri()); |
| ASSERT_FALSE(mock_report_sender.latest_report().empty()); |
| EXPECT_EQ("application/json; charset=utf-8", |
| mock_report_sender.latest_content_type()); |
| base::Value::Dict report_dict = |
| base::test::ParseJsonDict(mock_report_sender.latest_report()); |
| ASSERT_FALSE(report_dict.empty()); |
| std::string* report_hostname = report_dict.FindString("hostname"); |
| ASSERT_TRUE(report_hostname); |
| EXPECT_EQ(test_server_hostname, *report_hostname); |
| EXPECT_EQ(isolation_info.network_anonymization_key(), |
| mock_report_sender.latest_network_anonymization_key()); |
| } |
| |
| // Tests that reports do not get sent on requests to static pkp hosts that |
| // don't have pin violations. |
| TEST_F(URLRequestTestHTTP, ProcessPKPWithNoViolation) { |
| base::test::ScopedFeatureList scoped_feature_list_; |
| scoped_feature_list_.InitAndEnableFeature( |
| net::features::kStaticKeyPinningEnforcement); |
| EmbeddedTestServer https_test_server(net::EmbeddedTestServer::TYPE_HTTPS); |
| https_test_server.SetSSLConfig( |
| net::EmbeddedTestServer::CERT_COMMON_NAME_IS_DOMAIN); |
| https_test_server.ServeFilesFromSourceDirectory( |
| base::FilePath(kTestFilePath)); |
| ASSERT_TRUE(https_test_server.Start()); |
| |
| std::string test_server_hostname = kPKPHost; |
| |
| SetTransportSecurityStateSourceForTesting(&test_default::kHSTSSource); |
| |
| scoped_refptr<X509Certificate> cert = https_test_server.GetCertificate(); |
| ASSERT_TRUE(cert); |
| CertVerifyResult verify_result; |
| verify_result.verified_cert = cert; |
| verify_result.is_issued_by_known_root = true; |
| HashValue hash; |
| // The expected value of GoodPin1 used by |test_default::kHSTSSource|. |
| ASSERT_TRUE( |
| hash.FromString("sha256/Nn8jk5By4Vkq6BeOVZ7R7AC6XUUBZsWmUbJR1f1Y5FY=")); |
| verify_result.public_key_hashes.push_back(hash); |
| auto mock_cert_verifier = std::make_unique<MockCertVerifier>(); |
| mock_cert_verifier->AddResultForCert(cert.get(), verify_result, OK); |
| |
| MockCertificateReportSender mock_report_sender; // Must outlive `context`. |
| auto context_builder = CreateTestURLRequestContextBuilder(); |
| context_builder->SetCertVerifier(std::move(mock_cert_verifier)); |
| auto context = context_builder->Build(); |
| context->transport_security_state()->EnableStaticPinsForTesting(); |
| context->transport_security_state()->SetPinningListAlwaysTimelyForTesting( |
| true); |
| context->transport_security_state()->SetReportSender(&mock_report_sender); |
| |
| // Now send a request that does not trigger the violation. |
| TestDelegate d; |
| std::unique_ptr<URLRequest> request(context->CreateRequest( |
| https_test_server.GetURL(test_server_hostname, "/simple.html"), |
| DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| request->set_isolation_info(IsolationInfo::CreateTransient()); |
| request->Start(); |
| d.RunUntilComplete(); |
| |
| // Check that the request succeeded, a report was not sent and the pkp was |
| // not bypassed. |
| EXPECT_EQ(OK, d.request_status()); |
| EXPECT_EQ(GURL(), mock_report_sender.latest_report_uri()); |
| EXPECT_EQ(std::string(), mock_report_sender.latest_report()); |
| EXPECT_EQ(NetworkAnonymizationKey(), |
| mock_report_sender.latest_network_anonymization_key()); |
| TransportSecurityState::PKPState pkp_state; |
| EXPECT_TRUE(context->transport_security_state()->GetStaticPKPState( |
| test_server_hostname, &pkp_state)); |
| EXPECT_TRUE(pkp_state.HasPublicKeyPins()); |
| EXPECT_FALSE(request->ssl_info().pkp_bypassed); |
| } |
| |
| TEST_F(URLRequestTestHTTP, PKPBypassRecorded) { |
| base::test::ScopedFeatureList scoped_feature_list_; |
| scoped_feature_list_.InitAndEnableFeature( |
| net::features::kStaticKeyPinningEnforcement); |
| EmbeddedTestServer https_test_server(net::EmbeddedTestServer::TYPE_HTTPS); |
| https_test_server.SetSSLConfig( |
| net::EmbeddedTestServer::CERT_COMMON_NAME_IS_DOMAIN); |
| https_test_server.ServeFilesFromSourceDirectory( |
| base::FilePath(kTestFilePath)); |
| ASSERT_TRUE(https_test_server.Start()); |
| |
| // Set up a MockCertVerifier to be a local root that violates the pin |
| scoped_refptr<X509Certificate> cert = https_test_server.GetCertificate(); |
| ASSERT_TRUE(cert); |
| |
| CertVerifyResult verify_result; |
| verify_result.verified_cert = cert; |
| verify_result.is_issued_by_known_root = false; |
| HashValue hash; |
| ASSERT_TRUE( |
| hash.FromString("sha256/1111111111111111111111111111111111111111111=")); |
| verify_result.public_key_hashes.push_back(hash); |
| auto cert_verifier = std::make_unique<MockCertVerifier>(); |
| cert_verifier->AddResultForCert(cert.get(), verify_result, OK); |
| |
| std::string test_server_hostname = kPKPHost; |
| |
| SetTransportSecurityStateSourceForTesting(&test_default::kHSTSSource); |
| |
| MockCertificateReportSender mock_report_sender; // Must outlive `context`. |
| auto context_builder = CreateTestURLRequestContextBuilder(); |
| context_builder->SetCertVerifier(std::move(cert_verifier)); |
| auto context = context_builder->Build(); |
| context->transport_security_state()->EnableStaticPinsForTesting(); |
| context->transport_security_state()->SetPinningListAlwaysTimelyForTesting( |
| true); |
| context->transport_security_state()->SetReportSender(&mock_report_sender); |
| |
| TestDelegate d; |
| std::unique_ptr<URLRequest> request(context->CreateRequest( |
| https_test_server.GetURL(test_server_hostname, "/simple.html"), |
| DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| request->set_isolation_info(IsolationInfo::CreateTransient()); |
| request->Start(); |
| d.RunUntilComplete(); |
| |
| // Check that the request succeeded, a report was not sent and the PKP was |
| // bypassed. |
| EXPECT_EQ(OK, d.request_status()); |
| EXPECT_EQ(GURL(), mock_report_sender.latest_report_uri()); |
| EXPECT_EQ(std::string(), mock_report_sender.latest_report()); |
| EXPECT_EQ(NetworkAnonymizationKey(), |
| mock_report_sender.latest_network_anonymization_key()); |
| TransportSecurityState::PKPState pkp_state; |
| EXPECT_TRUE(context->transport_security_state()->GetStaticPKPState( |
| test_server_hostname, &pkp_state)); |
| EXPECT_TRUE(pkp_state.HasPublicKeyPins()); |
| EXPECT_TRUE(request->ssl_info().pkp_bypassed); |
| } |
| |
| TEST_F(URLRequestTestHTTP, ProcessSTSOnce) { |
| EmbeddedTestServer https_test_server(net::EmbeddedTestServer::TYPE_HTTPS); |
| https_test_server.SetSSLConfig( |
| net::EmbeddedTestServer::CERT_COMMON_NAME_IS_DOMAIN); |
| https_test_server.ServeFilesFromSourceDirectory( |
| base::FilePath(kTestFilePath)); |
| ASSERT_TRUE(https_test_server.Start()); |
| |
| std::string test_server_hostname = https_test_server.GetURL("/").host(); |
| |
| TestDelegate d; |
| std::unique_ptr<URLRequest> request(default_context().CreateRequest( |
| https_test_server.GetURL("/hsts-multiple-headers.html"), DEFAULT_PRIORITY, |
| &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| request->Start(); |
| d.RunUntilComplete(); |
| |
| // We should have set parameters from the first header, not the second. |
| TransportSecurityState* security_state = |
| default_context().transport_security_state(); |
| TransportSecurityState::STSState sts_state; |
| EXPECT_TRUE( |
| security_state->GetDynamicSTSState(test_server_hostname, &sts_state)); |
| EXPECT_EQ(TransportSecurityState::STSState::MODE_FORCE_HTTPS, |
| sts_state.upgrade_mode); |
| EXPECT_FALSE(sts_state.include_subdomains); |
| EXPECT_FALSE(sts_state.include_subdomains); |
| } |
| |
| #endif // !BUILDFLAG(IS_IOS) |
| |
| #if BUILDFLAG(ENABLE_REPORTING) |
| |
| TEST_F(URLRequestTestHTTP, NetworkErrorLogging_DontReportIfNetworkNotAccessed) { |
| EmbeddedTestServer https_test_server(net::EmbeddedTestServer::TYPE_HTTPS); |
| RegisterDefaultHandlers(&https_test_server); |
| ASSERT_TRUE(https_test_server.Start()); |
| GURL request_url = https_test_server.GetURL("/cachetime"); |
| |
| auto context_builder = CreateTestURLRequestContextBuilder(); |
| context_builder->set_network_error_logging_enabled(true); |
| auto& nel_service = *context_builder->SetNetworkErrorLoggingServiceForTesting( |
| std::make_unique<TestNetworkErrorLoggingService>()); |
| auto context = context_builder->Build(); |
| |
| // Populate the cache. |
| TestDelegate d; |
| std::unique_ptr<URLRequest> request(context->CreateRequest( |
| request_url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| request->set_isolation_info(isolation_info1_); |
| request->Start(); |
| d.RunUntilComplete(); |
| |
| ASSERT_EQ(1u, nel_service.errors().size()); |
| const TestNetworkErrorLoggingService::RequestDetails& error = |
| nel_service.errors()[0]; |
| EXPECT_EQ(request_url, error.uri); |
| EXPECT_EQ(200, error.status_code); |
| EXPECT_EQ(OK, error.type); |
| |
| request = context->CreateRequest(request_url, DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS); |
| request->set_isolation_info(isolation_info1_); |
| request->Start(); |
| d.RunUntilComplete(); |
| |
| EXPECT_FALSE(request->response_info().network_accessed); |
| EXPECT_TRUE(request->response_info().was_cached); |
| // No additional NEL report was generated. |
| EXPECT_EQ(1u, nel_service.errors().size()); |
| } |
| |
| TEST_F(URLRequestTestHTTP, NetworkErrorLogging_BasicSuccess) { |
| EmbeddedTestServer https_test_server(net::EmbeddedTestServer::TYPE_HTTPS); |
| https_test_server.ServeFilesFromSourceDirectory( |
| base::FilePath(kTestFilePath)); |
| ASSERT_TRUE(https_test_server.Start()); |
| GURL request_url = https_test_server.GetURL("/simple.html"); |
| |
| auto context_builder = CreateTestURLRequestContextBuilder(); |
| context_builder->set_network_error_logging_enabled(true); |
| auto& nel_service = *context_builder->SetNetworkErrorLoggingServiceForTesting( |
| std::make_unique<TestNetworkErrorLoggingService>()); |
| auto context = context_builder->Build(); |
| |
| TestDelegate d; |
| std::unique_ptr<URLRequest> request(context->CreateRequest( |
| request_url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| request->Start(); |
| d.RunUntilComplete(); |
| |
| ASSERT_EQ(1u, nel_service.errors().size()); |
| const TestNetworkErrorLoggingService::RequestDetails& error = |
| nel_service.errors()[0]; |
| EXPECT_EQ(request_url, error.uri); |
| EXPECT_EQ(200, error.status_code); |
| EXPECT_EQ(OK, error.type); |
| } |
| |
| TEST_F(URLRequestTestHTTP, NetworkErrorLogging_BasicError) { |
| EmbeddedTestServer https_test_server(net::EmbeddedTestServer::TYPE_HTTPS); |
| RegisterDefaultHandlers(&https_test_server); |
| ASSERT_TRUE(https_test_server.Start()); |
| GURL request_url = https_test_server.GetURL("/close-socket"); |
| |
| auto context_builder = CreateTestURLRequestContextBuilder(); |
| context_builder->set_network_error_logging_enabled(true); |
| auto& nel_service = *context_builder->SetNetworkErrorLoggingServiceForTesting( |
| std::make_unique<TestNetworkErrorLoggingService>()); |
| auto context = context_builder->Build(); |
| |
| TestDelegate d; |
| std::unique_ptr<URLRequest> request(context->CreateRequest( |
| request_url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| request->Start(); |
| d.RunUntilComplete(); |
| |
| ASSERT_EQ(1u, nel_service.errors().size()); |
| const TestNetworkErrorLoggingService::RequestDetails& error = |
| nel_service.errors()[0]; |
| EXPECT_EQ(request_url, error.uri); |
| EXPECT_EQ(0, error.status_code); |
| EXPECT_EQ(ERR_EMPTY_RESPONSE, error.type); |
| } |
| |
| TEST_F(URLRequestTestHTTP, NetworkErrorLogging_Redirect) { |
| EmbeddedTestServer https_test_server(net::EmbeddedTestServer::TYPE_HTTPS); |
| https_test_server.ServeFilesFromSourceDirectory( |
| base::FilePath(kTestFilePath)); |
| ASSERT_TRUE(https_test_server.Start()); |
| GURL request_url = https_test_server.GetURL("/redirect-test.html"); |
| GURL redirect_url = https_test_server.GetURL("/with-headers.html"); |
| |
| auto context_builder = CreateTestURLRequestContextBuilder(); |
| context_builder->set_network_error_logging_enabled(true); |
| auto& nel_service = *context_builder->SetNetworkErrorLoggingServiceForTesting( |
| std::make_unique<TestNetworkErrorLoggingService>()); |
| auto context = context_builder->Build(); |
| |
| TestDelegate d; |
| std::unique_ptr<URLRequest> request(context->CreateRequest( |
| request_url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| request->Start(); |
| d.RunUntilComplete(); |
| |
| ASSERT_EQ(2u, nel_service.errors().size()); |
| const TestNetworkErrorLoggingService::RequestDetails& error1 = |
| nel_service.errors()[0]; |
| EXPECT_EQ(request_url, error1.uri); |
| EXPECT_EQ(302, error1.status_code); |
| EXPECT_EQ(OK, error1.type); |
| const TestNetworkErrorLoggingService::RequestDetails& error2 = |
| nel_service.errors()[1]; |
| EXPECT_EQ(redirect_url, error2.uri); |
| EXPECT_EQ(200, error2.status_code); |
| EXPECT_EQ(OK, error2.type); |
| } |
| |
| TEST_F(URLRequestTestHTTP, NetworkErrorLogging_RedirectWithoutLocationHeader) { |
| EmbeddedTestServer https_test_server(net::EmbeddedTestServer::TYPE_HTTPS); |
| https_test_server.ServeFilesFromSourceDirectory( |
| base::FilePath(kTestFilePath)); |
| ASSERT_TRUE(https_test_server.Start()); |
| GURL request_url = https_test_server.GetURL("/308-without-location-header"); |
| |
| auto context_builder = CreateTestURLRequestContextBuilder(); |
| context_builder->set_network_error_logging_enabled(true); |
| auto& nel_service = *context_builder->SetNetworkErrorLoggingServiceForTesting( |
| std::make_unique<TestNetworkErrorLoggingService>()); |
| auto context = context_builder->Build(); |
| |
| TestDelegate d; |
| std::unique_ptr<URLRequest> request(context->CreateRequest( |
| request_url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| request->Start(); |
| d.RunUntilComplete(); |
| |
| ASSERT_EQ(1u, nel_service.errors().size()); |
| const TestNetworkErrorLoggingService::RequestDetails& error = |
| nel_service.errors()[0]; |
| EXPECT_EQ(request_url, error.uri); |
| EXPECT_EQ(308, error.status_code); |
| // The body of the response was successfully read. |
| EXPECT_EQ(OK, error.type); |
| } |
| |
| TEST_F(URLRequestTestHTTP, NetworkErrorLogging_Auth) { |
| EmbeddedTestServer https_test_server(net::EmbeddedTestServer::TYPE_HTTPS); |
| RegisterDefaultHandlers(&https_test_server); |
| ASSERT_TRUE(https_test_server.Start()); |
| GURL request_url = https_test_server.GetURL("/auth-basic"); |
| |
| auto context_builder = CreateTestURLRequestContextBuilder(); |
| context_builder->set_network_error_logging_enabled(true); |
| auto& nel_service = *context_builder->SetNetworkErrorLoggingServiceForTesting( |
| std::make_unique<TestNetworkErrorLoggingService>()); |
| auto context = context_builder->Build(); |
| |
| TestDelegate d; |
| d.set_credentials(AuthCredentials(kUser, kSecret)); |
| std::unique_ptr<URLRequest> request(context->CreateRequest( |
| request_url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| request->Start(); |
| d.RunUntilComplete(); |
| |
| ASSERT_EQ(2u, nel_service.errors().size()); |
| const TestNetworkErrorLoggingService::RequestDetails& error1 = |
| nel_service.errors()[0]; |
| EXPECT_EQ(request_url, error1.uri); |
| EXPECT_EQ(401, error1.status_code); |
| EXPECT_EQ(OK, error1.type); |
| const TestNetworkErrorLoggingService::RequestDetails& error2 = |
| nel_service.errors()[1]; |
| EXPECT_EQ(request_url, error2.uri); |
| EXPECT_EQ(200, error2.status_code); |
| EXPECT_EQ(OK, error2.type); |
| } |
| |
| TEST_F(URLRequestTestHTTP, NetworkErrorLogging_304Response) { |
| EmbeddedTestServer https_test_server(net::EmbeddedTestServer::TYPE_HTTPS); |
| RegisterDefaultHandlers(&https_test_server); |
| ASSERT_TRUE(https_test_server.Start()); |
| GURL request_url = https_test_server.GetURL("/auth-basic"); |
| |
| auto context_builder = CreateTestURLRequestContextBuilder(); |
| context_builder->set_network_error_logging_enabled(true); |
| auto& nel_service = *context_builder->SetNetworkErrorLoggingServiceForTesting( |
| std::make_unique<TestNetworkErrorLoggingService>()); |
| auto context = context_builder->Build(); |
| |
| // populate the cache |
| { |
| TestDelegate d; |
| d.set_credentials(AuthCredentials(kUser, kSecret)); |
| std::unique_ptr<URLRequest> r(context->CreateRequest( |
| request_url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| r->set_isolation_info(isolation_info1_); |
| r->Start(); |
| d.RunUntilComplete(); |
| } |
| ASSERT_EQ(2u, nel_service.errors().size()); |
| const TestNetworkErrorLoggingService::RequestDetails& error1 = |
| nel_service.errors()[0]; |
| EXPECT_EQ(request_url, error1.uri); |
| EXPECT_EQ(401, error1.status_code); |
| EXPECT_EQ(OK, error1.type); |
| const TestNetworkErrorLoggingService::RequestDetails& error2 = |
| nel_service.errors()[1]; |
| EXPECT_EQ(request_url, error2.uri); |
| EXPECT_EQ(200, error2.status_code); |
| EXPECT_EQ(OK, error2.type); |
| |
| // repeat request with end-to-end validation. since auth-basic results in a |
| // cachable page, we expect this test to result in a 304. in which case, the |
| // response should be fetched from the cache. |
| { |
| TestDelegate d; |
| d.set_credentials(AuthCredentials(kUser, kSecret)); |
| std::unique_ptr<URLRequest> r(context->CreateRequest( |
| request_url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| r->SetLoadFlags(LOAD_VALIDATE_CACHE); |
| r->set_isolation_info(isolation_info1_); |
| r->Start(); |
| d.RunUntilComplete(); |
| |
| // Should be the same cached document. |
| EXPECT_TRUE(r->was_cached()); |
| } |
| ASSERT_EQ(3u, nel_service.errors().size()); |
| const TestNetworkErrorLoggingService::RequestDetails& error3 = |
| nel_service.errors()[2]; |
| EXPECT_EQ(request_url, error3.uri); |
| EXPECT_EQ(304, error3.status_code); |
| EXPECT_EQ(OK, error3.type); |
| } |
| |
| TEST_F(URLRequestTestHTTP, NetworkErrorLogging_CancelInResponseStarted) { |
| EmbeddedTestServer https_test_server(net::EmbeddedTestServer::TYPE_HTTPS); |
| https_test_server.ServeFilesFromSourceDirectory( |
| base::FilePath(kTestFilePath)); |
| ASSERT_TRUE(https_test_server.Start()); |
| GURL request_url = https_test_server.GetURL("/simple.html"); |
| |
| auto context_builder = CreateTestURLRequestContextBuilder(); |
| context_builder->set_network_error_logging_enabled(true); |
| auto& nel_service = *context_builder->SetNetworkErrorLoggingServiceForTesting( |
| std::make_unique<TestNetworkErrorLoggingService>()); |
| auto context = context_builder->Build(); |
| |
| TestDelegate d; |
| d.set_cancel_in_response_started(true); |
| std::unique_ptr<URLRequest> request(context->CreateRequest( |
| request_url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| request->Start(); |
| d.RunUntilComplete(); |
| |
| ASSERT_EQ(1u, nel_service.errors().size()); |
| const TestNetworkErrorLoggingService::RequestDetails& error = |
| nel_service.errors()[0]; |
| EXPECT_EQ(request_url, error.uri); |
| EXPECT_EQ(200, error.status_code); |
| // Headers were received and the body should have been read but was not. |
| EXPECT_EQ(ERR_ABORTED, error.type); |
| } |
| |
| TEST_F(URLRequestTestHTTP, NetworkErrorLogging_CancelOnDataReceived) { |
| EmbeddedTestServer https_test_server(net::EmbeddedTestServer::TYPE_HTTPS); |
| https_test_server.ServeFilesFromSourceDirectory( |
| base::FilePath(kTestFilePath)); |
| ASSERT_TRUE(https_test_server.Start()); |
| GURL request_url = https_test_server.GetURL("/simple.html"); |
| |
| auto context_builder = CreateTestURLRequestContextBuilder(); |
| context_builder->set_network_error_logging_enabled(true); |
| auto& nel_service = *context_builder->SetNetworkErrorLoggingServiceForTesting( |
| std::make_unique<TestNetworkErrorLoggingService>()); |
| auto context = context_builder->Build(); |
| |
| TestDelegate d; |
| d.set_cancel_in_received_data(true); |
| std::unique_ptr<URLRequest> request(context->CreateRequest( |
| request_url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| request->Start(); |
| d.RunUntilComplete(); |
| |
| ASSERT_EQ(1u, nel_service.errors().size()); |
| const TestNetworkErrorLoggingService::RequestDetails& error = |
| nel_service.errors()[0]; |
| EXPECT_EQ(request_url, error.uri); |
| EXPECT_EQ(200, error.status_code); |
| // Data was received but the body was not completely read. |
| EXPECT_EQ(ERR_ABORTED, error.type); |
| } |
| |
| TEST_F(URLRequestTestHTTP, NetworkErrorLogging_CancelRedirect) { |
| EmbeddedTestServer https_test_server(net::EmbeddedTestServer::TYPE_HTTPS); |
| https_test_server.ServeFilesFromSourceDirectory( |
| base::FilePath(kTestFilePath)); |
| ASSERT_TRUE(https_test_server.Start()); |
| GURL request_url = https_test_server.GetURL("/redirect-test.html"); |
| |
| auto context_builder = CreateTestURLRequestContextBuilder(); |
| context_builder->set_network_error_logging_enabled(true); |
| auto& nel_service = *context_builder->SetNetworkErrorLoggingServiceForTesting( |
| std::make_unique<TestNetworkErrorLoggingService>()); |
| auto context = context_builder->Build(); |
| |
| TestDelegate d; |
| d.set_cancel_in_received_redirect(true); |
| std::unique_ptr<URLRequest> request(context->CreateRequest( |
| request_url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| request->Start(); |
| d.RunUntilComplete(); |
| |
| ASSERT_EQ(1u, nel_service.errors().size()); |
| const TestNetworkErrorLoggingService::RequestDetails& error = |
| nel_service.errors()[0]; |
| EXPECT_EQ(request_url, error.uri); |
| EXPECT_EQ(302, error.status_code); |
| // A valid HTTP response was received, even though the request was cancelled. |
| EXPECT_EQ(OK, error.type); |
| } |
| |
| #endif // BUILDFLAG(ENABLE_REPORTING) |
| |
| TEST_F(URLRequestTestHTTP, ContentTypeNormalizationTest) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req(default_context().CreateRequest( |
| http_test_server()->GetURL("/content-type-normalization.html"), |
| DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->Start(); |
| d.RunUntilComplete(); |
| |
| std::string mime_type; |
| req->GetMimeType(&mime_type); |
| EXPECT_EQ("text/html", mime_type); |
| |
| std::string charset; |
| req->GetCharset(&charset); |
| EXPECT_EQ("utf-8", charset); |
| req->Cancel(); |
| } |
| |
| TEST_F(URLRequestTestHTTP, FileRedirect) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req(default_context().CreateRequest( |
| http_test_server()->GetURL("/redirect-to-file.html"), DEFAULT_PRIORITY, |
| &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->Start(); |
| d.RunUntilComplete(); |
| |
| EXPECT_EQ(ERR_UNKNOWN_URL_SCHEME, d.request_status()); |
| EXPECT_EQ(1, d.received_redirect_count()); |
| } |
| |
| TEST_F(URLRequestTestHTTP, DataRedirect) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req(default_context().CreateRequest( |
| http_test_server()->GetURL("/redirect-to-data.html"), DEFAULT_PRIORITY, |
| &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->Start(); |
| d.RunUntilComplete(); |
| |
| EXPECT_EQ(ERR_UNKNOWN_URL_SCHEME, d.request_status()); |
| EXPECT_EQ(1, d.received_redirect_count()); |
| } |
| |
| TEST_F(URLRequestTestHTTP, RestrictUnsafeRedirect) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req(default_context().CreateRequest( |
| http_test_server()->GetURL( |
| "/server-redirect?unsafe://here-there-be-dragons"), |
| DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->Start(); |
| d.RunUntilComplete(); |
| |
| EXPECT_EQ(ERR_UNSAFE_REDIRECT, d.request_status()); |
| |
| // The redirect should have been rejected before reporting it to the |
| // caller. See https://crbug.com/723796 |
| EXPECT_EQ(0, d.received_redirect_count()); |
| } |
| |
| // Test that redirects to invalid URLs are rejected. See |
| // https://crbug.com/462272. |
| TEST_F(URLRequestTestHTTP, RedirectToInvalidURL) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req(default_context().CreateRequest( |
| http_test_server()->GetURL("/redirect-to-invalid-url.html"), |
| DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->Start(); |
| d.RunUntilComplete(); |
| |
| EXPECT_EQ(1, d.response_started_count()); |
| EXPECT_EQ(ERR_INVALID_REDIRECT, d.request_status()); |
| |
| // The redirect should have been rejected before reporting it to the caller. |
| EXPECT_EQ(0, d.received_redirect_count()); |
| } |
| |
| // Make sure redirects are cached, despite not reading their bodies. |
| TEST_F(URLRequestTestHTTP, CacheRedirect) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| GURL redirect_url = |
| http_test_server()->GetURL("/redirect302-to-echo-cacheable"); |
| |
| { |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req(default_context().CreateRequest( |
| redirect_url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->set_isolation_info(isolation_info1_); |
| req->Start(); |
| d.RunUntilComplete(); |
| EXPECT_EQ(OK, d.request_status()); |
| EXPECT_EQ(1, d.received_redirect_count()); |
| EXPECT_EQ(http_test_server()->GetURL("/echo"), req->url()); |
| } |
| |
| { |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req(default_context().CreateRequest( |
| redirect_url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->set_isolation_info(isolation_info1_); |
| req->Start(); |
| d.RunUntilRedirect(); |
| |
| EXPECT_EQ(1, d.received_redirect_count()); |
| EXPECT_EQ(0, d.response_started_count()); |
| EXPECT_TRUE(req->was_cached()); |
| |
| req->FollowDeferredRedirect(absl::nullopt /* removed_headers */, |
| absl::nullopt /* modified_headers */); |
| d.RunUntilComplete(); |
| EXPECT_EQ(1, d.received_redirect_count()); |
| EXPECT_EQ(1, d.response_started_count()); |
| EXPECT_EQ(OK, d.request_status()); |
| EXPECT_EQ(http_test_server()->GetURL("/echo"), req->url()); |
| } |
| } |
| |
| // Make sure a request isn't cached when a NetworkDelegate forces a redirect |
| // when the headers are read, since the body won't have been read. |
| TEST_F(URLRequestTestHTTP, NoCacheOnNetworkDelegateRedirect) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| // URL that is normally cached. |
| GURL initial_url = http_test_server()->GetURL("/cachetime"); |
| |
| { |
| // Set up the TestNetworkDelegate tp force a redirect. |
| GURL redirect_to_url = http_test_server()->GetURL("/echo"); |
| default_network_delegate().set_redirect_on_headers_received_url( |
| redirect_to_url); |
| |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req(default_context().CreateRequest( |
| initial_url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->Start(); |
| d.RunUntilComplete(); |
| EXPECT_EQ(OK, d.request_status()); |
| EXPECT_EQ(1, d.received_redirect_count()); |
| EXPECT_EQ(redirect_to_url, req->url()); |
| } |
| |
| { |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req(default_context().CreateRequest( |
| initial_url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->Start(); |
| d.RunUntilComplete(); |
| |
| EXPECT_EQ(OK, d.request_status()); |
| EXPECT_FALSE(req->was_cached()); |
| EXPECT_EQ(0, d.received_redirect_count()); |
| EXPECT_EQ(initial_url, req->url()); |
| } |
| } |
| |
| // Check that |preserve_fragment_on_redirect_url| is respected. |
| TEST_F(URLRequestTestHTTP, PreserveFragmentOnRedirectUrl) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| GURL original_url(http_test_server()->GetURL("/original#fragment1")); |
| GURL preserve_fragement_url(http_test_server()->GetURL("/echo")); |
| |
| default_network_delegate().set_redirect_on_headers_received_url( |
| preserve_fragement_url); |
| default_network_delegate().set_preserve_fragment_on_redirect_url( |
| preserve_fragement_url); |
| |
| TestDelegate d; |
| { |
| std::unique_ptr<URLRequest> r(default_context().CreateRequest( |
| original_url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| |
| r->Start(); |
| d.RunUntilComplete(); |
| |
| EXPECT_EQ(2U, r->url_chain().size()); |
| EXPECT_EQ(OK, d.request_status()); |
| EXPECT_EQ(original_url, r->original_url()); |
| EXPECT_EQ(preserve_fragement_url, r->url()); |
| } |
| } |
| |
| // Check that |preserve_fragment_on_redirect_url| has no effect when it doesn't |
| // match the URL being redirected to. |
| TEST_F(URLRequestTestHTTP, PreserveFragmentOnRedirectUrlMismatch) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| GURL original_url(http_test_server()->GetURL("/original#fragment1")); |
| GURL preserve_fragement_url(http_test_server()->GetURL("/echo#fragment2")); |
| GURL redirect_url(http_test_server()->GetURL("/echo")); |
| GURL expected_url(http_test_server()->GetURL("/echo#fragment1")); |
| |
| default_network_delegate().set_redirect_on_headers_received_url(redirect_url); |
| default_network_delegate().set_preserve_fragment_on_redirect_url( |
| preserve_fragement_url); |
| |
| TestDelegate d; |
| { |
| std::unique_ptr<URLRequest> r(default_context().CreateRequest( |
| original_url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| |
| r->Start(); |
| d.RunUntilComplete(); |
| |
| EXPECT_EQ(2U, r->url_chain().size()); |
| EXPECT_EQ(OK, d.request_status()); |
| EXPECT_EQ(original_url, r->original_url()); |
| EXPECT_EQ(expected_url, r->url()); |
| } |
| } |
| |
| // When a URLRequestRedirectJob is created, the redirection must be followed and |
| // the reference fragment of the target URL must not be modified. |
| TEST_F(URLRequestTestHTTP, RedirectJobWithReferenceFragment) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| GURL original_url( |
| http_test_server()->GetURL("/original#should-not-be-appended")); |
| GURL redirect_url(http_test_server()->GetURL("/echo")); |
| |
| TestDelegate d; |
| std::unique_ptr<URLRequest> r(default_context().CreateRequest( |
| original_url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| |
| std::unique_ptr<URLRequestRedirectJob> job = |
| std::make_unique<URLRequestRedirectJob>( |
| r.get(), redirect_url, RedirectUtil::ResponseCode::REDIRECT_302_FOUND, |
| "Very Good Reason"); |
| TestScopedURLInterceptor interceptor(r->url(), std::move(job)); |
| |
| r->Start(); |
| d.RunUntilComplete(); |
| |
| EXPECT_EQ(OK, d.request_status()); |
| EXPECT_EQ(original_url, r->original_url()); |
| EXPECT_EQ(redirect_url, r->url()); |
| } |
| |
| TEST_F(URLRequestTestHTTP, UnsupportedReferrerScheme) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| const std::string referrer("foobar://totally.legit.referrer"); |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req(default_context().CreateRequest( |
| http_test_server()->GetURL("/echoheader?Referer"), DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->SetReferrer(referrer); |
| req->Start(); |
| d.RunUntilComplete(); |
| |
| EXPECT_EQ(std::string("None"), d.data_received()); |
| } |
| |
| TEST_F(URLRequestTestHTTP, NoUserPassInReferrer) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req(default_context().CreateRequest( |
| http_test_server()->GetURL("/echoheader?Referer"), DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->SetReferrer("http://user:pass@foo.com/"); |
| req->Start(); |
| d.RunUntilComplete(); |
| |
| EXPECT_EQ(std::string("http://foo.com/"), d.data_received()); |
| } |
| |
| TEST_F(URLRequestTestHTTP, NoFragmentInReferrer) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req(default_context().CreateRequest( |
| http_test_server()->GetURL("/echoheader?Referer"), DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->SetReferrer("http://foo.com/test#fragment"); |
| req->Start(); |
| d.RunUntilComplete(); |
| |
| EXPECT_EQ(std::string("http://foo.com/test"), d.data_received()); |
| } |
| |
| TEST_F(URLRequestTestHTTP, EmptyReferrerAfterValidReferrer) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req(default_context().CreateRequest( |
| http_test_server()->GetURL("/echoheader?Referer"), DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->SetReferrer("http://foo.com/test#fragment"); |
| req->SetReferrer(""); |
| req->Start(); |
| d.RunUntilComplete(); |
| |
| EXPECT_EQ(std::string("None"), d.data_received()); |
| } |
| |
| TEST_F(URLRequestTestHTTP, CapRefererHeaderLength) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| // Verify that referrers over 4k are stripped to an origin, and referrers at |
| // or under 4k are unmodified. |
| { |
| std::string original_header = "http://example.com/"; |
| original_header.resize(4097, 'a'); |
| |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req(default_context().CreateRequest( |
| http_test_server()->GetURL("/echoheader?Referer"), DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->SetReferrer(original_header); |
| req->Start(); |
| d.RunUntilComplete(); |
| |
| // The request's referrer will be stripped since (1) there will be a |
| // mismatch between the request's referrer and the output of |
| // URLRequestJob::ComputeReferrerForPolicy and (2) the delegate, when |
| // offered the opportunity to cancel the request for this reason, will |
| // decline. |
| EXPECT_EQ("None", d.data_received()); |
| } |
| { |
| std::string original_header = "http://example.com/"; |
| original_header.resize(4096, 'a'); |
| |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req(default_context().CreateRequest( |
| http_test_server()->GetURL("/echoheader?Referer"), DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->SetReferrer(original_header); |
| req->Start(); |
| d.RunUntilComplete(); |
| |
| EXPECT_EQ(original_header, d.data_received()); |
| } |
| { |
| std::string original_header = "http://example.com/"; |
| original_header.resize(4095, 'a'); |
| |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req(default_context().CreateRequest( |
| http_test_server()->GetURL("/echoheader?Referer"), DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->SetReferrer(original_header); |
| req->Start(); |
| d.RunUntilComplete(); |
| |
| EXPECT_EQ(original_header, d.data_received()); |
| } |
| } |
| |
| TEST_F(URLRequestTestHTTP, CancelRedirect) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| TestDelegate d; |
| { |
| d.set_cancel_in_received_redirect(true); |
| std::unique_ptr<URLRequest> req(default_context().CreateRequest( |
| http_test_server()->GetURL("/redirect-test.html"), DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->Start(); |
| d.RunUntilComplete(); |
| |
| EXPECT_EQ(1, d.response_started_count()); |
| EXPECT_EQ(0, d.bytes_received()); |
| EXPECT_FALSE(d.received_data_before_response()); |
| EXPECT_EQ(ERR_ABORTED, d.request_status()); |
| } |
| } |
| |
| TEST_F(URLRequestTestHTTP, DeferredRedirect) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| TestDelegate d; |
| { |
| GURL test_url(http_test_server()->GetURL("/redirect-test.html")); |
| std::unique_ptr<URLRequest> req(default_context().CreateRequest( |
| test_url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| |
| req->Start(); |
| d.RunUntilRedirect(); |
| |
| EXPECT_EQ(1, d.received_redirect_count()); |
| |
| req->FollowDeferredRedirect(absl::nullopt /* removed_headers */, |
| absl::nullopt /* modified_headers */); |
| d.RunUntilComplete(); |
| |
| EXPECT_EQ(1, d.response_started_count()); |
| EXPECT_FALSE(d.received_data_before_response()); |
| EXPECT_EQ(OK, d.request_status()); |
| |
| base::FilePath path; |
| base::PathService::Get(base::DIR_SOURCE_ROOT, &path); |
| path = path.Append(kTestFilePath); |
| path = path.Append(FILE_PATH_LITERAL("with-headers.html")); |
| |
| std::string contents; |
| EXPECT_TRUE(base::ReadFileToString(path, &contents)); |
| EXPECT_EQ(contents, d.data_received()); |
| } |
| } |
| |
| TEST_F(URLRequestTestHTTP, DeferredRedirect_ModifiedHeaders) { |
| test_server::HttpRequest http_request; |
| int num_observed_requests = 0; |
| http_test_server()->RegisterRequestMonitor( |
| base::BindLambdaForTesting([&](const test_server::HttpRequest& request) { |
| http_request = request; |
| ++num_observed_requests; |
| })); |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| TestDelegate d; |
| { |
| GURL test_url(http_test_server()->GetURL("/redirect-test.html")); |
| std::unique_ptr<URLRequest> req(default_context().CreateRequest( |
| test_url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| |
| // Set initial headers for the request. |
| req->SetExtraRequestHeaderByName("Header1", "Value1", true /* overwrite */); |
| req->SetExtraRequestHeaderByName("Header2", "Value2", true /* overwrite */); |
| |
| req->Start(); |
| d.RunUntilRedirect(); |
| |
| // Initial request should only have initial headers. |
| EXPECT_EQ(1, d.received_redirect_count()); |
| EXPECT_EQ(1, num_observed_requests); |
| EXPECT_EQ("Value1", http_request.headers["Header1"]); |
| EXPECT_EQ("Value2", http_request.headers["Header2"]); |
| EXPECT_EQ(0u, http_request.headers.count("Header3")); |
| |
| // Overwrite Header2 and add Header3. |
| net::HttpRequestHeaders modified_headers; |
| modified_headers.SetHeader("Header2", ""); |
| modified_headers.SetHeader("Header3", "Value3"); |
| |
| req->FollowDeferredRedirect(absl::nullopt /* removed_headers */, |
| modified_headers); |
| d.RunUntilComplete(); |
| |
| EXPECT_EQ(1, d.response_started_count()); |
| EXPECT_FALSE(d.received_data_before_response()); |
| EXPECT_EQ(OK, d.request_status()); |
| |
| // Redirected request should also have modified headers. |
| EXPECT_EQ(2, num_observed_requests); |
| EXPECT_EQ("Value1", http_request.headers["Header1"]); |
| EXPECT_EQ(1u, http_request.headers.count("Header2")); |
| EXPECT_EQ("", http_request.headers["Header2"]); |
| EXPECT_EQ("Value3", http_request.headers["Header3"]); |
| } |
| } |
| |
| TEST_F(URLRequestTestHTTP, DeferredRedirect_RemovedHeaders) { |
| test_server::HttpRequest http_request; |
| int num_observed_requests = 0; |
| http_test_server()->RegisterRequestMonitor( |
| base::BindLambdaForTesting([&](const test_server::HttpRequest& request) { |
| http_request = request; |
| ++num_observed_requests; |
| })); |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| TestDelegate d; |
| { |
| GURL test_url(http_test_server()->GetURL("/redirect-test.html")); |
| std::unique_ptr<URLRequest> req(default_context().CreateRequest( |
| test_url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| |
| // Set initial headers for the request. |
| req->SetExtraRequestHeaderByName("Header1", "Value1", true /* overwrite */); |
| req->SetExtraRequestHeaderByName("Header2", "Value2", true /* overwrite */); |
| |
| req->Start(); |
| d.RunUntilRedirect(); |
| |
| // Initial request should have initial headers. |
| EXPECT_EQ(1, d.received_redirect_count()); |
| EXPECT_EQ(1, num_observed_requests); |
| EXPECT_EQ("Value1", http_request.headers["Header1"]); |
| EXPECT_EQ("Value2", http_request.headers["Header2"]); |
| |
| // Keep Header1 and remove Header2. |
| std::vector<std::string> removed_headers({"Header2"}); |
| req->FollowDeferredRedirect(removed_headers, |
| absl::nullopt /* modified_headers */); |
| d.RunUntilComplete(); |
| |
| EXPECT_EQ(1, d.response_started_count()); |
| EXPECT_FALSE(d.received_data_before_response()); |
| EXPECT_EQ(OK, d.request_status()); |
| |
| // Redirected request should have modified headers. |
| EXPECT_EQ(2, num_observed_requests); |
| EXPECT_EQ("Value1", http_request.headers["Header1"]); |
| EXPECT_EQ(0u, http_request.headers.count("Header2")); |
| } |
| } |
| |
| TEST_F(URLRequestTestHTTP, CancelDeferredRedirect) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| TestDelegate d; |
| { |
| std::unique_ptr<URLRequest> req(default_context().CreateRequest( |
| http_test_server()->GetURL("/redirect-test.html"), DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->Start(); |
| d.RunUntilRedirect(); |
| |
| EXPECT_EQ(1, d.received_redirect_count()); |
| |
| req->Cancel(); |
| d.RunUntilComplete(); |
| |
| EXPECT_EQ(1, d.response_started_count()); |
| EXPECT_EQ(0, d.bytes_received()); |
| EXPECT_FALSE(d.received_data_before_response()); |
| EXPECT_EQ(ERR_ABORTED, d.request_status()); |
| } |
| } |
| |
| TEST_F(URLRequestTestHTTP, VaryHeader) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| // Populate the cache. |
| { |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req(default_context().CreateRequest( |
| http_test_server()->GetURL("/echoheadercache?foo"), DEFAULT_PRIORITY, |
| &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| HttpRequestHeaders headers; |
| headers.SetHeader("foo", "1"); |
| req->SetExtraRequestHeaders(headers); |
| req->set_isolation_info(isolation_info1_); |
| req->Start(); |
| d.RunUntilComplete(); |
| |
| LoadTimingInfo load_timing_info; |
| req->GetLoadTimingInfo(&load_timing_info); |
| TestLoadTimingNotReused(load_timing_info, CONNECT_TIMING_HAS_DNS_TIMES); |
| } |
| |
| // Expect a cache hit. |
| { |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req(default_context().CreateRequest( |
| http_test_server()->GetURL("/echoheadercache?foo"), DEFAULT_PRIORITY, |
| &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| HttpRequestHeaders headers; |
| headers.SetHeader("foo", "1"); |
| req->SetExtraRequestHeaders(headers); |
| req->set_isolation_info(isolation_info1_); |
| req->Start(); |
| d.RunUntilComplete(); |
| |
| EXPECT_TRUE(req->was_cached()); |
| |
| LoadTimingInfo load_timing_info; |
| req->GetLoadTimingInfo(&load_timing_info); |
| TestLoadTimingCacheHitNoNetwork(load_timing_info); |
| } |
| |
| // Expect a cache miss. |
| { |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req(default_context().CreateRequest( |
| http_test_server()->GetURL("/echoheadercache?foo"), DEFAULT_PRIORITY, |
| &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| HttpRequestHeaders headers; |
| headers.SetHeader("foo", "2"); |
| req->SetExtraRequestHeaders(headers); |
| req->set_isolation_info(isolation_info1_); |
| req->Start(); |
| d.RunUntilComplete(); |
| |
| EXPECT_FALSE(req->was_cached()); |
| |
| LoadTimingInfo load_timing_info; |
| req->GetLoadTimingInfo(&load_timing_info); |
| TestLoadTimingNotReused(load_timing_info, CONNECT_TIMING_HAS_DNS_TIMES); |
| } |
| } |
| |
| TEST_F(URLRequestTestHTTP, BasicAuth) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| // populate the cache |
| { |
| TestDelegate d; |
| d.set_credentials(AuthCredentials(kUser, kSecret)); |
| |
| std::unique_ptr<URLRequest> r(default_context().CreateRequest( |
| http_test_server()->GetURL("/auth-basic"), DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| r->set_isolation_info(isolation_info1_); |
| r->Start(); |
| |
| d.RunUntilComplete(); |
| |
| EXPECT_TRUE(d.data_received().find("user/secret") != std::string::npos); |
| } |
| |
| // repeat request with end-to-end validation. since auth-basic results in a |
| // cachable page, we expect this test to result in a 304. in which case, the |
| // response should be fetched from the cache. |
| { |
| TestDelegate d; |
| d.set_credentials(AuthCredentials(kUser, kSecret)); |
| |
| std::unique_ptr<URLRequest> r(default_context().CreateRequest( |
| http_test_server()->GetURL("/auth-basic"), DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| r->SetLoadFlags(LOAD_VALIDATE_CACHE); |
| r->set_isolation_info(isolation_info1_); |
| r->Start(); |
| |
| d.RunUntilComplete(); |
| |
| EXPECT_TRUE(d.data_received().find("user/secret") != std::string::npos); |
| |
| // Should be the same cached document. |
| EXPECT_TRUE(r->was_cached()); |
| } |
| } |
| |
| // Check that Set-Cookie headers in 401 responses are respected. |
| // http://crbug.com/6450 |
| TEST_F(URLRequestTestHTTP, BasicAuthWithCookies) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| GURL url_requiring_auth = |
| http_test_server()->GetURL("/auth-basic?set-cookie-if-challenged"); |
| |
| // Request a page that will give a 401 containing a Set-Cookie header. |
| // Verify that when the transaction is restarted, it includes the new cookie. |
| TestDelegate d; |
| d.set_credentials(AuthCredentials(kUser, kSecret)); |
| |
| std::unique_ptr<URLRequest> r = |
| CreateFirstPartyRequest(default_context(), url_requiring_auth, &d); |
| r->Start(); |
| |
| d.RunUntilComplete(); |
| |
| EXPECT_TRUE(d.data_received().find("user/secret") != std::string::npos); |
| |
| // Make sure we sent the cookie in the restarted transaction. |
| EXPECT_TRUE(d.data_received().find("Cookie: got_challenged=true") != |
| std::string::npos); |
| } |
| |
| // Same test as above, except this time the restart is initiated earlier |
| // (without user intervention since identity is embedded in the URL). |
| TEST_F(URLRequestTestHTTP, BasicAuthWithCredentialsWithCookies) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| GURL url_requiring_auth = |
| http_test_server()->GetURL("/auth-basic?set-cookie-if-challenged"); |
| GURL::Replacements replacements; |
| replacements.SetUsernameStr("user2"); |
| replacements.SetPasswordStr("secret"); |
| GURL url_with_identity = url_requiring_auth.ReplaceComponents(replacements); |
| |
| TestDelegate d; |
| |
| std::unique_ptr<URLRequest> r = |
| CreateFirstPartyRequest(default_context(), url_with_identity, &d); |
| r->Start(); |
| |
| d.RunUntilComplete(); |
| |
| EXPECT_TRUE(d.data_received().find("user2/secret") != std::string::npos); |
| |
| // Make sure we sent the cookie in the restarted transaction. |
| EXPECT_TRUE(d.data_received().find("Cookie: got_challenged=true") != |
| std::string::npos); |
| } |
| |
| TEST_F(URLRequestTestHTTP, BasicAuthWithCookiesCancelAuth) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| GURL url_requiring_auth = |
| http_test_server()->GetURL("/auth-basic?set-cookie-if-challenged"); |
| |
| // Request a page that will give a 401 containing a Set-Cookie header. |
| // Verify that cookies are set before credentials are provided, and then |
| // cancelling auth does not result in setting the cookies again. |
| TestDelegate d; |
| |
| EXPECT_TRUE(GetAllCookies(&default_context()).empty()); |
| |
| std::unique_ptr<URLRequest> r = |
| CreateFirstPartyRequest(default_context(), url_requiring_auth, &d); |
| r->Start(); |
| d.RunUntilAuthRequired(); |
| |
| // Cookie should have been set. |
| EXPECT_EQ(1, default_network_delegate().set_cookie_count()); |
| CookieList cookies = GetAllCookies(&default_context()); |
| ASSERT_EQ(1u, cookies.size()); |
| EXPECT_EQ("got_challenged", cookies[0].Name()); |
| EXPECT_EQ("true", cookies[0].Value()); |
| |
| // Delete cookie. |
| default_context().cookie_store()->DeleteAllAsync( |
| CookieStore::DeleteCallback()); |
| |
| // Cancel auth and continue the request. |
| r->CancelAuth(); |
| d.RunUntilComplete(); |
| ASSERT_TRUE(r->response_headers()); |
| EXPECT_EQ(401, r->response_headers()->response_code()); |
| |
| // Cookie should not have been set again. |
| EXPECT_TRUE(GetAllCookies(&default_context()).empty()); |
| EXPECT_EQ(1, default_network_delegate().set_cookie_count()); |
| } |
| |
| // Tests the IsolationInfo is updated approiately on redirect. |
| TEST_F(URLRequestTestHTTP, IsolationInfoUpdatedOnRedirect) { |
| base::test::ScopedFeatureList feature_list; |
| feature_list.InitAndEnableFeature( |
| net::features::kSplitCacheByNetworkIsolationKey); |
| |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| GURL redirect_url = |
| http_test_server()->GetURL("redirected.test", "/cachetime"); |
| GURL original_url = http_test_server()->GetURL( |
| "original.test", "/server-redirect?" + redirect_url.spec()); |
| |
| url::Origin original_origin = url::Origin::Create(original_url); |
| url::Origin redirect_origin = url::Origin::Create(redirect_url); |
| |
| // Since transient IsolationInfos use opaque origins, need to create a single |
| // consistent transient origin one for be used as the original and updated |
| // info in the same test case. |
| IsolationInfo transient_isolation_info = IsolationInfo::CreateTransient(); |
| |
| const struct { |
| IsolationInfo info_before_redirect; |
| IsolationInfo expected_info_after_redirect; |
| } kTestCases[] = { |
| {IsolationInfo(), IsolationInfo()}, |
| {IsolationInfo::Create(IsolationInfo::RequestType::kMainFrame, |
| original_origin, original_origin, |
| SiteForCookies()), |
| IsolationInfo::Create(IsolationInfo::RequestType::kMainFrame, |
| redirect_origin, redirect_origin, |
| SiteForCookies::FromOrigin(redirect_origin))}, |
| {IsolationInfo::Create(IsolationInfo::RequestType::kSubFrame, |
| original_origin, original_origin, |
| SiteForCookies::FromOrigin(original_origin)), |
| IsolationInfo::Create(IsolationInfo::RequestType::kSubFrame, |
| original_origin, redirect_origin, |
| SiteForCookies::FromOrigin(original_origin))}, |
| {IsolationInfo::Create(IsolationInfo::RequestType::kOther, |
| original_origin, original_origin, |
| SiteForCookies()), |
| IsolationInfo::Create(IsolationInfo::RequestType::kOther, |
| original_origin, original_origin, |
| SiteForCookies())}, |
| {transient_isolation_info, transient_isolation_info}, |
| }; |
| |
| for (const auto& test_case : kTestCases) { |
| // Populate the cache, using the expected final IsolationInfo. |
| { |
| TestDelegate d; |
| |
| std::unique_ptr<URLRequest> r(default_context().CreateRequest( |
| redirect_url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| r->set_isolation_info(test_case.expected_info_after_redirect); |
| r->Start(); |
| d.RunUntilComplete(); |
| EXPECT_THAT(d.request_status(), IsOk()); |
| } |
| |
| // Send a request using the initial IsolationInfo that should be redirected |
| // to the cached url, and should use the cached entry if the NIK was |
| // updated, except in the case the IsolationInfo's NIK was empty. |
| { |
| TestDelegate d; |
| |
| std::unique_ptr<URLRequest> r(default_context().CreateRequest( |
| original_url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| r->set_isolation_info(test_case.info_before_redirect); |
| r->Start(); |
| d.RunUntilComplete(); |
| EXPECT_THAT(d.request_status(), IsOk()); |
| EXPECT_EQ(redirect_url, r->url()); |
| |
| EXPECT_EQ(!test_case.expected_info_after_redirect.network_isolation_key() |
| .IsTransient(), |
| r->was_cached()); |
| EXPECT_EQ(test_case.expected_info_after_redirect.request_type(), |
| r->isolation_info().request_type()); |
| EXPECT_EQ(test_case.expected_info_after_redirect.top_frame_origin(), |
| r->isolation_info().top_frame_origin()); |
| EXPECT_EQ(test_case.expected_info_after_redirect.frame_origin(), |
| r->isolation_info().frame_origin()); |
| EXPECT_EQ(test_case.expected_info_after_redirect.network_isolation_key(), |
| r->isolation_info().network_isolation_key()); |
| EXPECT_TRUE(test_case.expected_info_after_redirect.site_for_cookies() |
| .IsEquivalent(r->isolation_info().site_for_cookies())); |
| } |
| } |
| } |
| |
| // Tests that |key_auth_cache_by_network_anonymization_key| is respected. |
| TEST_F(URLRequestTestHTTP, AuthWithNetworkAnonymizationKey) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| for (bool key_auth_cache_by_network_anonymization_key : {false, true}) { |
| auto context_builder = CreateTestURLRequestContextBuilder(); |
| HttpNetworkSessionParams network_session_params; |
| network_session_params |
| .key_auth_cache_server_entries_by_network_anonymization_key = |
| key_auth_cache_by_network_anonymization_key; |
| context_builder->set_http_network_session_params(network_session_params); |
| auto context = context_builder->Build(); |
| |
| // Populate the auth cache using one NetworkAnonymizationKey. |
| { |
| TestDelegate d; |
| GURL url(base::StringPrintf( |
| "http://%s:%s@%s/auth-basic", base::UTF16ToASCII(kUser).c_str(), |
| base::UTF16ToASCII(kSecret).c_str(), |
| http_test_server()->host_port_pair().ToString().c_str())); |
| |
| std::unique_ptr<URLRequest> r(context->CreateRequest( |
| url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| r->SetLoadFlags(LOAD_BYPASS_CACHE); |
| r->set_isolation_info(isolation_info1_); |
| r->Start(); |
| |
| d.RunUntilComplete(); |
| EXPECT_THAT(d.request_status(), IsOk()); |
| ASSERT_TRUE(r->response_headers()); |
| EXPECT_EQ(200, r->response_headers()->response_code()); |
| EXPECT_TRUE(d.data_received().find("user/secret") != std::string::npos); |
| } |
| |
| // Make a request with another NetworkAnonymizationKey. This may or may not |
| // use the cached auth credentials, depending on whether or not the |
| // HttpAuthCache is configured to respect the NetworkAnonymizationKey. |
| { |
| TestDelegate d; |
| |
| std::unique_ptr<URLRequest> r(context->CreateRequest( |
| http_test_server()->GetURL("/auth-basic"), DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| r->SetLoadFlags(LOAD_BYPASS_CACHE); |
| r->set_isolation_info(isolation_info2_); |
| r->Start(); |
| |
| d.RunUntilComplete(); |
| |
| EXPECT_THAT(d.request_status(), IsOk()); |
| ASSERT_TRUE(r->response_headers()); |
| if (key_auth_cache_by_network_anonymization_key) { |
| EXPECT_EQ(401, r->response_headers()->response_code()); |
| } else { |
| EXPECT_EQ(200, r->response_headers()->response_code()); |
| } |
| |
| EXPECT_EQ(!key_auth_cache_by_network_anonymization_key, |
| d.data_received().find("user/secret") != std::string::npos); |
| } |
| } |
| } |
| |
| TEST_F(URLRequestTest, ReportCookieActivity) { |
| HttpTestServer test_server; |
| ASSERT_TRUE(test_server.Start()); |
| |
| RecordingNetLogObserver net_log_observer; |
| auto context_builder = CreateTestURLRequestContextBuilder(); |
| auto& network_delegate = *context_builder->set_network_delegate( |
| std::make_unique<FilteringTestNetworkDelegate>()); |
| network_delegate.SetCookieFilter("not_stored_cookie"); |
| network_delegate.set_block_annotate_cookies(); |
| context_builder->set_net_log(net::NetLog::Get()); |
| auto context = context_builder->Build(); |
| // Make sure cookies blocked from being stored are caught, and those that are |
| // accepted are reported as well. |
| GURL set_cookie_test_url = test_server.GetURL( |
| "/set-cookie?not_stored_cookie=true&" |
| "stored_cookie=tasty" |
| "&path_cookie=narrow;path=/set-cookie"); |
| { |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req = |
| CreateFirstPartyRequest(*context, set_cookie_test_url, &d); |
| req->Start(); |
| d.RunUntilComplete(); |
| |
| ASSERT_EQ(3u, req->maybe_stored_cookies().size()); |
| EXPECT_EQ("not_stored_cookie", |
| req->maybe_stored_cookies()[0].cookie->Name()); |
| EXPECT_TRUE(req->maybe_stored_cookies()[0] |
| .access_result.status.HasExactlyExclusionReasonsForTesting( |
| {CookieInclusionStatus::EXCLUDE_USER_PREFERENCES})); |
| EXPECT_EQ("stored_cookie", req->maybe_stored_cookies()[1].cookie->Name()); |
| EXPECT_TRUE( |
| req->maybe_stored_cookies()[1].access_result.status.IsInclude()); |
| EXPECT_EQ("stored_cookie", req->maybe_stored_cookies()[1].cookie->Name()); |
| EXPECT_TRUE( |
| req->maybe_stored_cookies()[2].access_result.status.IsInclude()); |
| EXPECT_EQ("path_cookie", req->maybe_stored_cookies()[2].cookie->Name()); |
| auto entries = net_log_observer.GetEntriesWithType( |
| NetLogEventType::COOKIE_INCLUSION_STATUS); |
| EXPECT_EQ(3u, entries.size()); |
| EXPECT_EQ("{\"domain\":\"" + set_cookie_test_url.host() + |
| "\",\"name\":\"not_stored_cookie\",\"operation\":\"store\"," |
| "\"path\":\"/\",\"status\":\"EXCLUDE_USER_PREFERENCES, " |
| "DO_NOT_WARN\"}", |
| SerializeNetLogValueToJson(entries[0].params)); |
| EXPECT_EQ("{\"domain\":\"" + set_cookie_test_url.host() + |
| "\",\"name\":\"stored_cookie\",\"operation\":\"store\"," |
| "\"path\":\"/\",\"status\":\"INCLUDE, DO_NOT_WARN\"}", |
| SerializeNetLogValueToJson(entries[1].params)); |
| EXPECT_EQ( |
| "{\"domain\":\"" + set_cookie_test_url.host() + |
| "\",\"name\":\"path_cookie\",\"operation\":\"store\"," |
| "\"path\":\"/set-cookie\",\"status\":\"INCLUDE, DO_NOT_WARN\"}", |
| SerializeNetLogValueToJson(entries[2].params)); |
| net_log_observer.Clear(); |
| } |
| { |
| TestDelegate d; |
| // Make sure cookies blocked from being sent are caught. |
| GURL test_url = test_server.GetURL("/echoheader?Cookie"); |
| std::unique_ptr<URLRequest> req = |
| CreateFirstPartyRequest(*context, test_url, &d); |
| req->Start(); |
| d.RunUntilComplete(); |
| |
| EXPECT_TRUE(d.data_received().find("stored_cookie=tasty") == |
| std::string::npos); |
| |
| ASSERT_EQ(2u, req->maybe_sent_cookies().size()); |
| EXPECT_EQ("path_cookie", req->maybe_sent_cookies()[0].cookie.Name()); |
| EXPECT_TRUE( |
| req->maybe_sent_cookies()[0] |
| .access_result.status.HasExactlyExclusionReasonsForTesting( |
| {net::CookieInclusionStatus::EXCLUDE_NOT_ON_PATH, |
| net::CookieInclusionStatus::EXCLUDE_USER_PREFERENCES})); |
| EXPECT_EQ("stored_cookie", req->maybe_sent_cookies()[1].cookie.Name()); |
| EXPECT_TRUE( |
| req->maybe_sent_cookies()[1] |
| .access_result.status.HasExactlyExclusionReasonsForTesting( |
| {net::CookieInclusionStatus::EXCLUDE_USER_PREFERENCES})); |
| auto entries = net_log_observer.GetEntriesWithType( |
| NetLogEventType::COOKIE_INCLUSION_STATUS); |
| EXPECT_EQ(2u, entries.size()); |
| EXPECT_EQ("{\"domain\":\"" + set_cookie_test_url.host() + |
| "\",\"name\":\"path_cookie\",\"operation\":\"send\",\"path\":" |
| "\"/set-cookie\",\"status\":\"EXCLUDE_NOT_ON_PATH, " |
| "EXCLUDE_USER_PREFERENCES, DO_NOT_WARN\"}", |
| SerializeNetLogValueToJson(entries[0].params)); |
| EXPECT_EQ( |
| "{\"domain\":\"" + set_cookie_test_url.host() + |
| "\",\"name\":\"stored_cookie\",\"operation\":\"send\",\"path\":\"/" |
| "\",\"status\":\"EXCLUDE_USER_PREFERENCES, DO_NOT_WARN\"}", |
| SerializeNetLogValueToJson(entries[1].params)); |
| net_log_observer.Clear(); |
| } |
| { |
| TestDelegate d; |
| // Ensure that the log does not contain cookie names when not set to collect |
| // sensitive data. |
| net_log_observer.SetObserverCaptureMode(NetLogCaptureMode::kDefault); |
| |
| GURL test_url = test_server.GetURL("/echoheader?Cookie"); |
| std::unique_ptr<URLRequest> req = |
| CreateFirstPartyRequest(*context, test_url, &d); |
| req->Start(); |
| d.RunUntilComplete(); |
| |
| auto entries = net_log_observer.GetEntriesWithType( |
| NetLogEventType::COOKIE_INCLUSION_STATUS); |
| EXPECT_EQ(2u, entries.size()); |
| |
| // Ensure that the potentially-sensitive |name|, |domain|, and |path| fields |
| // are omitted, but other fields are logged as expected. |
| EXPECT_EQ( |
| "{\"operation\":\"send\",\"status\":\"EXCLUDE_NOT_ON_PATH, " |
| "EXCLUDE_USER_PREFERENCES, DO_NOT_WARN\"}", |
| SerializeNetLogValueToJson(entries[0].params)); |
| EXPECT_EQ( |
| "{\"operation\":\"send\",\"status\":\"EXCLUDE_USER_PREFERENCES, " |
| "DO_NOT_WARN\"}", |
| SerializeNetLogValueToJson(entries[1].params)); |
| |
| net_log_observer.Clear(); |
| net_log_observer.SetObserverCaptureMode( |
| NetLogCaptureMode::kIncludeSensitive); |
| } |
| |
| network_delegate.unset_block_annotate_cookies(); |
| { |
| // Now with sending cookies re-enabled, it should actually be sent. |
| TestDelegate d; |
| GURL test_url = test_server.GetURL("/echoheader?Cookie"); |
| std::unique_ptr<URLRequest> req = |
| CreateFirstPartyRequest(*context, test_url, &d); |
| req->Start(); |
| d.RunUntilComplete(); |
| |
| EXPECT_TRUE(d.data_received().find("stored_cookie=tasty") != |
| std::string::npos); |
| |
| ASSERT_EQ(2u, req->maybe_sent_cookies().size()); |
| EXPECT_EQ("path_cookie", req->maybe_sent_cookies()[0].cookie.Name()); |
| EXPECT_TRUE(req->maybe_sent_cookies()[0] |
| .access_result.status.HasExactlyExclusionReasonsForTesting( |
| {net::CookieInclusionStatus::EXCLUDE_NOT_ON_PATH})); |
| EXPECT_EQ("stored_cookie", req->maybe_sent_cookies()[1].cookie.Name()); |
| EXPECT_TRUE(req->maybe_sent_cookies()[1].access_result.status.IsInclude()); |
| auto entries = net_log_observer.GetEntriesWithType( |
| NetLogEventType::COOKIE_INCLUSION_STATUS); |
| EXPECT_EQ(2u, entries.size()); |
| EXPECT_EQ( |
| "{\"domain\":\"" + set_cookie_test_url.host() + |
| "\",\"name\":\"path_cookie\",\"operation\":\"send\",\"path\":\"/" |
| "set-cookie\",\"status\":\"EXCLUDE_NOT_ON_PATH, DO_NOT_WARN\"}", |
| SerializeNetLogValueToJson(entries[0].params)); |
| EXPECT_EQ("{\"domain\":\"" + set_cookie_test_url.host() + |
| "\",\"name\":\"stored_cookie\",\"operation\":\"send\"," |
| "\"path\":\"/\",\"status\":\"INCLUDE, DO_NOT_WARN\"}", |
| SerializeNetLogValueToJson(entries[1].params)); |
| net_log_observer.Clear(); |
| } |
| } |
| |
| // Test that the SameSite-by-default CookieInclusionStatus warnings do not get |
| // set if the cookie would have been rejected for other reasons. |
| // Regression test for https://crbug.com/1027318. |
| TEST_F(URLRequestTest, NoCookieInclusionStatusWarningIfWouldBeExcludedAnyway) { |
| HttpTestServer test_server; |
| ASSERT_TRUE(test_server.Start()); |
| |
| auto context_builder = CreateTestURLRequestContextBuilder(); |
| auto& network_delegate = *context_builder->set_network_delegate( |
| std::make_unique<FilteringTestNetworkDelegate>()); |
| network_delegate.SetCookieFilter("blockeduserpreference"); |
| context_builder->SetCookieStore( |
| std::make_unique<CookieMonster>(nullptr, nullptr)); |
| auto context = context_builder->Build(); |
| auto& cm = *static_cast<CookieMonster*>(context->cookie_store()); |
| |
| // Set cookies |
| { |
| // Attempt to set some cookies in a cross-site context without a SameSite |
| // attribute. They should all be blocked. Only the one that would have been |
| // included had it not been for the new SameSite features should have a |
| // warning attached. |
| TestDelegate d; |
| GURL test_url = test_server.GetURL("this.example", |
| "/set-cookie?blockeduserpreference=true&" |
| "unspecifiedsamesite=1&" |
| "invalidsecure=1;Secure"); |
| GURL cross_site_url = test_server.GetURL("other.example", "/"); |
| std::unique_ptr<URLRequest> req(context->CreateRequest( |
| test_url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->set_site_for_cookies( |
| net::SiteForCookies::FromUrl(cross_site_url)); // cross-site context |
| req->Start(); |
| d.RunUntilComplete(); |
| |
| ASSERT_EQ(3u, req->maybe_stored_cookies().size()); |
| |
| // Cookie blocked by user preferences is not warned about. |
| EXPECT_EQ("blockeduserpreference", |
| req->maybe_stored_cookies()[0].cookie->Name()); |
| // It doesn't pick up the EXCLUDE_UNSPECIFIED_TREATED_AS_LAX because it |
| // doesn't even make it to the cookie store (it is filtered out beforehand). |
| EXPECT_TRUE(req->maybe_stored_cookies()[0] |
| .access_result.status.HasExactlyExclusionReasonsForTesting( |
| {CookieInclusionStatus::EXCLUDE_USER_PREFERENCES})); |
| EXPECT_FALSE( |
| req->maybe_stored_cookies()[0].access_result.status.ShouldWarn()); |
| |
| // Cookie that would be included had it not been for the new SameSite rules |
| // is warned about. |
| EXPECT_EQ("unspecifiedsamesite", |
| req->maybe_stored_cookies()[1].cookie->Name()); |
| EXPECT_TRUE(req->maybe_stored_cookies()[1] |
| .access_result.status.HasExactlyExclusionReasonsForTesting( |
| {CookieInclusionStatus:: |
| EXCLUDE_SAMESITE_UNSPECIFIED_TREATED_AS_LAX})); |
| EXPECT_TRUE(req->maybe_stored_cookies()[1] |
| .access_result.status.HasExactlyWarningReasonsForTesting( |
| {CookieInclusionStatus:: |
| WARN_SAMESITE_UNSPECIFIED_CROSS_SITE_CONTEXT})); |
| |
| // Cookie that is blocked because of invalid Secure attribute is not warned |
| // about. |
| EXPECT_EQ("invalidsecure", req->maybe_stored_cookies()[2].cookie->Name()); |
| EXPECT_TRUE(req->maybe_stored_cookies()[2] |
| .access_result.status.HasExactlyExclusionReasonsForTesting( |
| {CookieInclusionStatus::EXCLUDE_SECURE_ONLY, |
| CookieInclusionStatus:: |
| EXCLUDE_SAMESITE_UNSPECIFIED_TREATED_AS_LAX})); |
| EXPECT_FALSE( |
| req->maybe_stored_cookies()[2].access_result.status.ShouldWarn()); |
| } |
| |
| // Get cookies (blocked by user preference) |
| network_delegate.set_block_annotate_cookies(); |
| { |
| GURL url = test_server.GetURL("/"); |
| auto cookie1 = CanonicalCookie::Create( |
| url, "cookienosamesite=1", base::Time::Now(), absl::nullopt, |
| absl::nullopt /* cookie_partition_key */); |
| base::RunLoop run_loop; |
| CookieAccessResult access_result; |
| cm.SetCanonicalCookieAsync( |
| std::move(cookie1), url, CookieOptions::MakeAllInclusive(), |
| base::BindLambdaForTesting([&](CookieAccessResult result) { |
| access_result = result; |
| run_loop.Quit(); |
| })); |
| run_loop.Run(); |
| EXPECT_TRUE(access_result.status.IsInclude()); |
| |
| TestDelegate d; |
| GURL test_url = test_server.GetURL("/echoheader?Cookie"); |
| std::unique_ptr<URLRequest> req(context->CreateRequest( |
| test_url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| GURL cross_site_url = test_server.GetURL("other.example", "/"); |
| req->set_site_for_cookies( |
| net::SiteForCookies::FromUrl(cross_site_url)); // cross-site context |
| req->Start(); |
| d.RunUntilComplete(); |
| |
| // No cookies were sent with the request because getting cookies is blocked. |
| EXPECT_EQ("None", d.data_received()); |
| ASSERT_EQ(1u, req->maybe_sent_cookies().size()); |
| EXPECT_EQ("cookienosamesite", req->maybe_sent_cookies()[0].cookie.Name()); |
| EXPECT_TRUE(req->maybe_sent_cookies()[0] |
| .access_result.status.HasExactlyExclusionReasonsForTesting( |
| {CookieInclusionStatus::EXCLUDE_USER_PREFERENCES, |
| CookieInclusionStatus:: |
| EXCLUDE_SAMESITE_UNSPECIFIED_TREATED_AS_LAX})); |
| // Cookie should not be warned about because it was blocked because of user |
| // preferences. |
| EXPECT_FALSE( |
| req->maybe_sent_cookies()[0].access_result.status.ShouldWarn()); |
| } |
| network_delegate.unset_block_annotate_cookies(); |
| |
| // Get cookies |
| { |
| GURL url = test_server.GetURL("/"); |
| auto cookie2 = CanonicalCookie::Create( |
| url, "cookiewithpath=1;path=/foo", base::Time::Now(), absl::nullopt, |
| absl::nullopt /* cookie_partition_key */); |
| base::RunLoop run_loop; |
| // Note: cookie1 from the previous testcase is still in the cookie store. |
| CookieAccessResult access_result; |
| cm.SetCanonicalCookieAsync( |
| std::move(cookie2), url, CookieOptions::MakeAllInclusive(), |
| base::BindLambdaForTesting([&](CookieAccessResult result) { |
| access_result = result; |
| run_loop.Quit(); |
| })); |
| run_loop.Run(); |
| EXPECT_TRUE(access_result.status.IsInclude()); |
| |
| TestDelegate d; |
| GURL test_url = test_server.GetURL("/echoheader?Cookie"); |
| std::unique_ptr<URLRequest> req(context->CreateRequest( |
| test_url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| GURL cross_site_url = test_server.GetURL("other.example", "/"); |
| req->set_site_for_cookies( |
| net::SiteForCookies::FromUrl(cross_site_url)); // cross-site context |
| req->Start(); |
| d.RunUntilComplete(); |
| |
| // No cookies were sent with the request because they don't specify SameSite |
| // and the request is cross-site. |
| EXPECT_EQ("None", d.data_received()); |
| ASSERT_EQ(2u, req->maybe_sent_cookies().size()); |
| // Cookie excluded for other reasons is not warned about. |
| // Note: this cookie is first because the cookies are sorted by path length |
| // with longest first. See CookieSorter() in cookie_monster.cc. |
| EXPECT_EQ("cookiewithpath", req->maybe_sent_cookies()[0].cookie.Name()); |
| EXPECT_TRUE(req->maybe_sent_cookies()[0] |
| .access_result.status.HasExactlyExclusionReasonsForTesting( |
| {CookieInclusionStatus::EXCLUDE_NOT_ON_PATH, |
| CookieInclusionStatus:: |
| EXCLUDE_SAMESITE_UNSPECIFIED_TREATED_AS_LAX})); |
| EXPECT_FALSE( |
| req->maybe_sent_cookies()[0].access_result.status.ShouldWarn()); |
| // Cookie that was only blocked because of unspecified SameSite should be |
| // warned about. |
| EXPECT_EQ("cookienosamesite", req->maybe_sent_cookies()[1].cookie.Name()); |
| EXPECT_TRUE(req->maybe_sent_cookies()[1] |
| .access_result.status.HasExactlyExclusionReasonsForTesting( |
| {CookieInclusionStatus:: |
| EXCLUDE_SAMESITE_UNSPECIFIED_TREATED_AS_LAX})); |
| EXPECT_TRUE(req->maybe_sent_cookies()[1] |
| .access_result.status.HasExactlyWarningReasonsForTesting( |
| {CookieInclusionStatus:: |
| WARN_SAMESITE_UNSPECIFIED_CROSS_SITE_CONTEXT})); |
| } |
| } |
| |
| TEST_F(URLRequestTestHTTP, AuthChallengeCancelCookieCollect) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| GURL url_requiring_auth = |
| http_test_server()->GetURL("/auth-basic?set-cookie-if-challenged"); |
| |
| auto context_builder = CreateTestURLRequestContextBuilder(); |
| auto filtering_network_delegate = |
| std::make_unique<FilteringTestNetworkDelegate>(); |
| filtering_network_delegate->SetCookieFilter("got_challenged"); |
| context_builder->set_network_delegate(std::move(filtering_network_delegate)); |
| auto context = context_builder->Build(); |
| |
| TestDelegate delegate; |
| |
| std::unique_ptr<URLRequest> request = |
| CreateFirstPartyRequest(*context, url_requiring_auth, &delegate); |
| request->Start(); |
| |
| delegate.RunUntilAuthRequired(); |
| ASSERT_EQ(1u, request->maybe_stored_cookies().size()); |
| EXPECT_TRUE(request->maybe_stored_cookies()[0] |
| .access_result.status.HasExactlyExclusionReasonsForTesting( |
| {net::CookieInclusionStatus::EXCLUDE_USER_PREFERENCES})); |
| EXPECT_EQ("got_challenged=true", |
| request->maybe_stored_cookies()[0].cookie_string); |
| |
| // This shouldn't DCHECK-fail. |
| request->CancelAuth(); |
| delegate.RunUntilComplete(); |
| } |
| |
| TEST_F(URLRequestTestHTTP, AuthChallengeWithFilteredCookies) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| GURL url_requiring_auth = |
| http_test_server()->GetURL("/auth-basic?set-cookie-if-challenged"); |
| GURL url_requiring_auth_wo_cookies = |
| http_test_server()->GetURL("/auth-basic"); |
| // Check maybe_stored_cookies is populated first round trip, and cleared on |
| // the second. |
| { |
| auto context_builder = CreateTestURLRequestContextBuilder(); |
| auto& filtering_network_delegate = *context_builder->set_network_delegate( |
| std::make_unique<FilteringTestNetworkDelegate>()); |
| filtering_network_delegate.SetCookieFilter("got_challenged"); |
| auto context = context_builder->Build(); |
| |
| TestDelegate delegate; |
| |
| std::unique_ptr<URLRequest> request = |
| CreateFirstPartyRequest(*context, url_requiring_auth, &delegate); |
| request->Start(); |
| |
| delegate.RunUntilAuthRequired(); |
| // Make sure it was blocked once. |
| EXPECT_EQ(1, filtering_network_delegate.blocked_set_cookie_count()); |
| |
| // The number of cookies blocked from the most recent round trip. |
| ASSERT_EQ(1u, request->maybe_stored_cookies().size()); |
| EXPECT_TRUE( |
| request->maybe_stored_cookies() |
| .front() |
| .access_result.status.HasExactlyExclusionReasonsForTesting( |
| {net::CookieInclusionStatus::EXCLUDE_USER_PREFERENCES})); |
| |
| // Now check the second round trip |
| request->SetAuth(AuthCredentials(kUser, kSecret)); |
| delegate.RunUntilComplete(); |
| EXPECT_THAT(delegate.request_status(), IsOk()); |
| |
| // There are DCHECKs in URLRequestHttpJob that would fail if |
| // maybe_sent_cookies and maybe_stored_cookies were not cleared properly. |
| |
| // Make sure the cookie was actually filtered and not sent. |
| EXPECT_EQ(std::string::npos, |
| delegate.data_received().find("Cookie: got_challenged=true")); |
| |
| // The number of cookies that most recent round trip tried to set. |
| ASSERT_EQ(0u, request->maybe_stored_cookies().size()); |
| } |
| |
| // Check maybe_sent_cookies on first round trip (and cleared for the second). |
| { |
| auto context_builder = CreateTestURLRequestContextBuilder(); |
| auto& filtering_network_delegate = *context_builder->set_network_delegate( |
| std::make_unique<FilteringTestNetworkDelegate>()); |
| filtering_network_delegate.set_block_annotate_cookies(); |
| context_builder->SetCookieStore( |
| std::make_unique<CookieMonster>(nullptr, nullptr)); |
| auto context = context_builder->Build(); |
| |
| auto* cm = static_cast<CookieMonster*>(context->cookie_store()); |
| auto another_cookie = CanonicalCookie::Create( |
| url_requiring_auth_wo_cookies, "another_cookie=true", base::Time::Now(), |
| absl::nullopt /* server_time */, |
| absl::nullopt /* cookie_partition_key */); |
| cm->SetCanonicalCookieAsync(std::move(another_cookie), |
| url_requiring_auth_wo_cookies, |
| net::CookieOptions::MakeAllInclusive(), |
| CookieStore::SetCookiesCallback()); |
| |
| TestDelegate delegate; |
| |
| std::unique_ptr<URLRequest> request = CreateFirstPartyRequest( |
| *context, url_requiring_auth_wo_cookies, &delegate); |
| request->Start(); |
| |
| delegate.RunUntilAuthRequired(); |
| |
| ASSERT_EQ(1u, request->maybe_sent_cookies().size()); |
| EXPECT_EQ("another_cookie", |
| request->maybe_sent_cookies().front().cookie.Name()); |
| EXPECT_EQ("true", request->maybe_sent_cookies().front().cookie.Value()); |
| EXPECT_TRUE( |
| request->maybe_sent_cookies() |
| .front() |
| .access_result.status.HasExactlyExclusionReasonsForTesting( |
| {net::CookieInclusionStatus::EXCLUDE_USER_PREFERENCES})); |
| |
| // Check maybe_sent_cookies on second roundtrip. |
| request->set_maybe_sent_cookies({}); |
| cm->DeleteAllAsync(CookieStore::DeleteCallback()); |
| auto one_more_cookie = CanonicalCookie::Create( |
| url_requiring_auth_wo_cookies, "one_more_cookie=true", |
| base::Time::Now(), absl::nullopt /* server_time */, |
| absl::nullopt /* cookie_partition_key */); |
| cm->SetCanonicalCookieAsync(std::move(one_more_cookie), |
| url_requiring_auth_wo_cookies, |
| net::CookieOptions::MakeAllInclusive(), |
| CookieStore::SetCookiesCallback()); |
| |
| request->SetAuth(AuthCredentials(kUser, kSecret)); |
| delegate.RunUntilComplete(); |
| EXPECT_THAT(delegate.request_status(), IsOk()); |
| |
| // There are DCHECKs in URLRequestHttpJob that would fail if |
| // maybe_sent_cookies and maybe_stored_cookies were not cleared properly. |
| |
| // Make sure the cookie was actually filtered. |
| EXPECT_EQ(std::string::npos, |
| delegate.data_received().find("Cookie: one_more_cookie=true")); |
| // got_challenged was set after the first request and blocked on the second, |
| // so it should only have been blocked this time |
| EXPECT_EQ(2, filtering_network_delegate.blocked_annotate_cookies_count()); |
| |
| // // The number of cookies blocked from the most recent round trip. |
| ASSERT_EQ(1u, request->maybe_sent_cookies().size()); |
| EXPECT_EQ("one_more_cookie", |
| request->maybe_sent_cookies().front().cookie.Name()); |
| EXPECT_TRUE( |
| request->maybe_sent_cookies() |
| .front() |
| .access_result.status.HasExactlyExclusionReasonsForTesting( |
| {net::CookieInclusionStatus::EXCLUDE_USER_PREFERENCES})); |
| } |
| } |
| |
| // Tests that load timing works as expected with auth and the cache. |
| TEST_F(URLRequestTestHTTP, BasicAuthLoadTiming) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| // populate the cache |
| { |
| TestDelegate d; |
| |
| std::unique_ptr<URLRequest> r(default_context().CreateRequest( |
| http_test_server()->GetURL("/auth-basic"), DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| r->set_isolation_info(isolation_info1_); |
| r->Start(); |
| d.RunUntilAuthRequired(); |
| |
| LoadTimingInfo load_timing_info_before_auth; |
| r->GetLoadTimingInfo(&load_timing_info_before_auth); |
| TestLoadTimingNotReused(load_timing_info_before_auth, |
| CONNECT_TIMING_HAS_DNS_TIMES); |
| |
| r->SetAuth(AuthCredentials(kUser, kSecret)); |
| d.RunUntilComplete(); |
| |
| EXPECT_TRUE(d.data_received().find("user/secret") != std::string::npos); |
| LoadTimingInfo load_timing_info; |
| r->GetLoadTimingInfo(&load_timing_info); |
| // The test server does not support keep alive sockets, so the second |
| // request with auth should use a new socket. |
| TestLoadTimingNotReused(load_timing_info, CONNECT_TIMING_HAS_DNS_TIMES); |
| EXPECT_NE(load_timing_info_before_auth.socket_log_id, |
| load_timing_info.socket_log_id); |
| EXPECT_LE(load_timing_info_before_auth.receive_headers_end, |
| load_timing_info.connect_timing.connect_start); |
| } |
| |
| // Repeat request with end-to-end validation. Since auth-basic results in a |
| // cachable page, we expect this test to result in a 304. In which case, the |
| // response should be fetched from the cache. |
| { |
| TestDelegate d; |
| d.set_credentials(AuthCredentials(kUser, kSecret)); |
| |
| std::unique_ptr<URLRequest> r(default_context().CreateRequest( |
| http_test_server()->GetURL("/auth-basic"), DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| r->SetLoadFlags(LOAD_VALIDATE_CACHE); |
| r->set_isolation_info(isolation_info1_); |
| r->Start(); |
| |
| d.RunUntilComplete(); |
| |
| EXPECT_TRUE(d.data_received().find("user/secret") != std::string::npos); |
| |
| // Should be the same cached document. |
| EXPECT_TRUE(r->was_cached()); |
| |
| // Since there was a request that went over the wire, the load timing |
| // information should include connection times. |
| LoadTimingInfo load_timing_info; |
| r->GetLoadTimingInfo(&load_timing_info); |
| TestLoadTimingNotReused(load_timing_info, CONNECT_TIMING_HAS_DNS_TIMES); |
| } |
| } |
| |
| // In this test, we do a POST which the server will 302 redirect. |
| // The subsequent transaction should use GET, and should not send the |
| // Content-Type header. |
| // http://code.google.com/p/chromium/issues/detail?id=843 |
| TEST_F(URLRequestTestHTTP, Post302RedirectGet) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| const char kData[] = "hello world"; |
| |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req(default_context().CreateRequest( |
| http_test_server()->GetURL("/redirect-to-echoall"), DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->set_method("POST"); |
| req->set_upload(CreateSimpleUploadData(kData)); |
| |
| // Set headers (some of which are specific to the POST). |
| HttpRequestHeaders headers; |
| headers.SetHeader("Content-Type", |
| "multipart/form-data;" |
| "boundary=----WebKitFormBoundaryAADeAA+NAAWMAAwZ"); |
| headers.SetHeader("Accept", |
| "text/xml,application/xml,application/xhtml+xml," |
| "text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5"); |
| headers.SetHeader("Accept-Language", "en-US,en"); |
| headers.SetHeader("Accept-Charset", "ISO-8859-1,*,utf-8"); |
| headers.SetHeader("Content-Length", "11"); |
| headers.SetHeader("Origin", "http://localhost:1337/"); |
| req->SetExtraRequestHeaders(headers); |
| req->Start(); |
| d.RunUntilComplete(); |
| |
| std::string mime_type; |
| req->GetMimeType(&mime_type); |
| EXPECT_EQ("text/html", mime_type); |
| |
| const std::string& data = d.data_received(); |
| |
| // Check that the post-specific headers were stripped: |
| EXPECT_FALSE(ContainsString(data, "Content-Length:")); |
| EXPECT_FALSE(ContainsString(data, "Content-Type:")); |
| EXPECT_FALSE(ContainsString(data, "Origin:")); |
| |
| // These extra request headers should not have been stripped. |
| EXPECT_TRUE(ContainsString(data, "Accept:")); |
| EXPECT_TRUE(ContainsString(data, "Accept-Language:")); |
| EXPECT_TRUE(ContainsString(data, "Accept-Charset:")); |
| } |
| |
| // The following tests check that we handle mutating the request for HTTP |
| // redirects as expected. |
| // See https://crbug.com/56373, https://crbug.com/102130, and |
| // https://crbug.com/465517. |
| |
| TEST_F(URLRequestTestHTTP, Redirect301Tests) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| const GURL url = http_test_server()->GetURL("/redirect301-to-echo"); |
| const GURL https_redirect_url = |
| http_test_server()->GetURL("/redirect301-to-https"); |
| |
| HTTPRedirectMethodTest(url, "POST", "GET", true); |
| HTTPRedirectMethodTest(url, "PUT", "PUT", true); |
| HTTPRedirectMethodTest(url, "HEAD", "HEAD", false); |
| |
| HTTPRedirectOriginHeaderTest(url, "GET", "GET", |
| url.DeprecatedGetOriginAsURL().spec()); |
| HTTPRedirectOriginHeaderTest(https_redirect_url, "GET", "GET", "null"); |
| HTTPRedirectOriginHeaderTest(url, "POST", "GET", std::string()); |
| HTTPRedirectOriginHeaderTest(https_redirect_url, "POST", "GET", |
| std::string()); |
| HTTPRedirectOriginHeaderTest(url, "PUT", "PUT", |
| url.DeprecatedGetOriginAsURL().spec()); |
| HTTPRedirectOriginHeaderTest(https_redirect_url, "PUT", "PUT", "null"); |
| } |
| |
| TEST_F(URLRequestTestHTTP, Redirect302Tests) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| const GURL url = http_test_server()->GetURL("/redirect302-to-echo"); |
| const GURL https_redirect_url = |
| http_test_server()->GetURL("/redirect302-to-https"); |
| |
| HTTPRedirectMethodTest(url, "POST", "GET", true); |
| HTTPRedirectMethodTest(url, "PUT", "PUT", true); |
| HTTPRedirectMethodTest(url, "HEAD", "HEAD", false); |
| |
| HTTPRedirectOriginHeaderTest(url, "GET", "GET", |
| url.DeprecatedGetOriginAsURL().spec()); |
| HTTPRedirectOriginHeaderTest(https_redirect_url, "GET", "GET", "null"); |
| HTTPRedirectOriginHeaderTest(url, "POST", "GET", std::string()); |
| HTTPRedirectOriginHeaderTest(https_redirect_url, "POST", "GET", |
| std::string()); |
| HTTPRedirectOriginHeaderTest(url, "PUT", "PUT", |
| url.DeprecatedGetOriginAsURL().spec()); |
| HTTPRedirectOriginHeaderTest(https_redirect_url, "PUT", "PUT", "null"); |
| } |
| |
| TEST_F(URLRequestTestHTTP, Redirect303Tests) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| const GURL url = http_test_server()->GetURL("/redirect303-to-echo"); |
| const GURL https_redirect_url = |
| http_test_server()->GetURL("/redirect303-to-https"); |
| |
| HTTPRedirectMethodTest(url, "POST", "GET", true); |
| HTTPRedirectMethodTest(url, "PUT", "GET", true); |
| HTTPRedirectMethodTest(url, "HEAD", "HEAD", false); |
| |
| HTTPRedirectOriginHeaderTest(url, "DELETE", "GET", std::string()); |
| HTTPRedirectOriginHeaderTest(https_redirect_url, "DELETE", "GET", |
| std::string()); |
| HTTPRedirectOriginHeaderTest(url, "GET", "GET", |
| url.DeprecatedGetOriginAsURL().spec()); |
| HTTPRedirectOriginHeaderTest(https_redirect_url, "GET", "GET", "null"); |
| HTTPRedirectOriginHeaderTest(url, "HEAD", "HEAD", |
| url.DeprecatedGetOriginAsURL().spec()); |
| HTTPRedirectOriginHeaderTest(https_redirect_url, "HEAD", "HEAD", "null"); |
| HTTPRedirectOriginHeaderTest(url, "OPTIONS", "GET", std::string()); |
| HTTPRedirectOriginHeaderTest(https_redirect_url, "OPTIONS", "GET", |
| std::string()); |
| HTTPRedirectOriginHeaderTest(url, "POST", "GET", std::string()); |
| HTTPRedirectOriginHeaderTest(https_redirect_url, "POST", "GET", |
| std::string()); |
| HTTPRedirectOriginHeaderTest(url, "PUT", "GET", std::string()); |
| HTTPRedirectOriginHeaderTest(https_redirect_url, "PUT", "GET", std::string()); |
| } |
| |
| TEST_F(URLRequestTestHTTP, Redirect307Tests) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| const GURL url = http_test_server()->GetURL("/redirect307-to-echo"); |
| const GURL https_redirect_url = |
| http_test_server()->GetURL("/redirect307-to-https"); |
| |
| HTTPRedirectMethodTest(url, "POST", "POST", true); |
| HTTPRedirectMethodTest(url, "PUT", "PUT", true); |
| HTTPRedirectMethodTest(url, "HEAD", "HEAD", false); |
| |
| HTTPRedirectOriginHeaderTest(url, "GET", "GET", |
| url.DeprecatedGetOriginAsURL().spec()); |
| HTTPRedirectOriginHeaderTest(https_redirect_url, "GET", "GET", "null"); |
| HTTPRedirectOriginHeaderTest(url, "POST", "POST", |
| url.DeprecatedGetOriginAsURL().spec()); |
| HTTPRedirectOriginHeaderTest(https_redirect_url, "POST", "POST", "null"); |
| HTTPRedirectOriginHeaderTest(url, "PUT", "PUT", |
| url.DeprecatedGetOriginAsURL().spec()); |
| HTTPRedirectOriginHeaderTest(https_redirect_url, "PUT", "PUT", "null"); |
| } |
| |
| TEST_F(URLRequestTestHTTP, Redirect308Tests) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| const GURL url = http_test_server()->GetURL("/redirect308-to-echo"); |
| const GURL https_redirect_url = |
| http_test_server()->GetURL("/redirect308-to-https"); |
| |
| HTTPRedirectMethodTest(url, "POST", "POST", true); |
| HTTPRedirectMethodTest(url, "PUT", "PUT", true); |
| HTTPRedirectMethodTest(url, "HEAD", "HEAD", false); |
| |
| HTTPRedirectOriginHeaderTest(url, "GET", "GET", |
| url.DeprecatedGetOriginAsURL().spec()); |
| HTTPRedirectOriginHeaderTest(https_redirect_url, "GET", "GET", "null"); |
| HTTPRedirectOriginHeaderTest(url, "POST", "POST", |
| url.DeprecatedGetOriginAsURL().spec()); |
| HTTPRedirectOriginHeaderTest(https_redirect_url, "POST", "POST", "null"); |
| HTTPRedirectOriginHeaderTest(url, "PUT", "PUT", |
| url.DeprecatedGetOriginAsURL().spec()); |
| HTTPRedirectOriginHeaderTest(https_redirect_url, "PUT", "PUT", "null"); |
| } |
| |
| // Make sure that 308 responses without bodies are not treated as redirects. |
| // Certain legacy apis that pre-date the response code expect this behavior |
| // (Like Google Drive). |
| TEST_F(URLRequestTestHTTP, NoRedirectOn308WithoutLocationHeader) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| TestDelegate d; |
| const GURL url = http_test_server()->GetURL("/308-without-location-header"); |
| |
| std::unique_ptr<URLRequest> request(default_context().CreateRequest( |
| url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| |
| request->Start(); |
| d.RunUntilComplete(); |
| EXPECT_EQ(OK, d.request_status()); |
| EXPECT_EQ(0, d.received_redirect_count()); |
| EXPECT_EQ(308, request->response_headers()->response_code()); |
| EXPECT_EQ("This is not a redirect.", d.data_received()); |
| } |
| |
| TEST_F(URLRequestTestHTTP, Redirect302PreserveReferenceFragment) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| GURL original_url( |
| http_test_server()->GetURL("/redirect302-to-echo#fragment")); |
| GURL expected_url(http_test_server()->GetURL("/echo#fragment")); |
| |
| TestDelegate d; |
| std::unique_ptr<URLRequest> r(default_context().CreateRequest( |
| original_url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| |
| r->Start(); |
| d.RunUntilComplete(); |
| |
| EXPECT_EQ(2U, r->url_chain().size()); |
| EXPECT_EQ(OK, d.request_status()); |
| EXPECT_EQ(original_url, r->original_url()); |
| EXPECT_EQ(expected_url, r->url()); |
| } |
| |
| TEST_F(URLRequestTestHTTP, RedirectWithFilteredCookies) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| // FilteringTestNetworkDelegate filters by name, so the names of the two |
| // cookies have to be the same. The values have been set to different strings |
| // (the value of the server-redirect cookies is "true" and set-cookie is |
| // "other") to differentiate between the two round trips. |
| GURL redirect_to( |
| http_test_server()->GetURL("/set-cookie?server-redirect=other")); |
| |
| GURL original_url(http_test_server()->GetURL("/server-redirect-with-cookie?" + |
| redirect_to.spec())); |
| |
| GURL original_url_wo_cookie( |
| http_test_server()->GetURL("/server-redirect?" + redirect_to.spec())); |
| // Check maybe_stored_cookies on first round trip. |
| { |
| auto context_builder = CreateTestURLRequestContextBuilder(); |
| auto& filtering_network_delegate = *context_builder->set_network_delegate( |
| std::make_unique<FilteringTestNetworkDelegate>()); |
| filtering_network_delegate.SetCookieFilter( |
| "server-redirect"); // Filter the cookie server-redirect sets. |
| auto context = context_builder->Build(); |
| |
| TestDelegate delegate; |
| std::unique_ptr<URLRequest> request = |
| CreateFirstPartyRequest(*context, original_url, &delegate); |
| |
| request->Start(); |
| delegate.RunUntilRedirect(); |
| |
| // Make sure it was blocked once. |
| EXPECT_EQ(1, filtering_network_delegate.blocked_set_cookie_count()); |
| |
| // The number of cookies blocked from the most recent round trip. |
| ASSERT_EQ(1u, request->maybe_stored_cookies().size()); |
| EXPECT_EQ("server-redirect", |
| request->maybe_stored_cookies().front().cookie->Name()); |
| EXPECT_EQ("true", request->maybe_stored_cookies().front().cookie->Value()); |
| EXPECT_TRUE( |
| request->maybe_stored_cookies() |
| .front() |
| .access_result.status.HasExactlyExclusionReasonsForTesting( |
| {net::CookieInclusionStatus::EXCLUDE_USER_PREFERENCES})); |
| |
| // Check maybe_stored_cookies on second round trip (and clearing from the |
| // first). |
| request->FollowDeferredRedirect(absl::nullopt, absl::nullopt); |
| delegate.RunUntilComplete(); |
| EXPECT_THAT(delegate.request_status(), IsOk()); |
| |
| // There are DCHECKs in URLRequestHttpJob that would fail if |
| // maybe_sent_cookies and maybe_stored_cookies we not cleared properly. |
| |
| // Make sure it was blocked twice. |
| EXPECT_EQ(2, filtering_network_delegate.blocked_set_cookie_count()); |
| |
| // The number of cookies blocked from the most recent round trip. |
| ASSERT_EQ(1u, request->maybe_stored_cookies().size()); |
| EXPECT_EQ("server-redirect", |
| request->maybe_stored_cookies().front().cookie->Name()); |
| EXPECT_EQ("other", request->maybe_stored_cookies().front().cookie->Value()); |
| EXPECT_TRUE( |
| request->maybe_stored_cookies() |
| .front() |
| .access_result.status.HasExactlyExclusionReasonsForTesting( |
| {net::CookieInclusionStatus::EXCLUDE_USER_PREFERENCES})); |
| } |
| |
| // Check maybe_sent_cookies on first round trip. |
| { |
| auto context_builder = CreateTestURLRequestContextBuilder(); |
| auto& filtering_network_delegate = *context_builder->set_network_delegate( |
| std::make_unique<FilteringTestNetworkDelegate>()); |
| filtering_network_delegate.set_block_annotate_cookies(); |
| context_builder->SetCookieStore( |
| std::make_unique<CookieMonster>(nullptr, nullptr)); |
| auto context = context_builder->Build(); |
| |
| auto* cm = static_cast<CookieMonster*>(context->cookie_store()); |
| auto another_cookie = CanonicalCookie::Create( |
| original_url, "another_cookie=true", base::Time::Now(), |
| absl::nullopt /* server_time */, |
| absl::nullopt /* cookie_partition_key */); |
| cm->SetCanonicalCookieAsync(std::move(another_cookie), original_url, |
| net::CookieOptions::MakeAllInclusive(), |
| CookieStore::SetCookiesCallback()); |
| |
| TestDelegate delegate; |
| std::unique_ptr<URLRequest> request = |
| CreateFirstPartyRequest(*context, original_url_wo_cookie, &delegate); |
| request->Start(); |
| |
| delegate.RunUntilRedirect(); |
| |
| ASSERT_EQ(1u, request->maybe_sent_cookies().size()); |
| EXPECT_EQ("another_cookie", |
| request->maybe_sent_cookies().front().cookie.Name()); |
| EXPECT_TRUE( |
| request->maybe_sent_cookies() |
| .front() |
| .access_result.status.HasExactlyExclusionReasonsForTesting( |
| {net::CookieInclusionStatus::EXCLUDE_USER_PREFERENCES})); |
| |
| // Check maybe_sent_cookies on second round trip |
| request->set_maybe_sent_cookies({}); |
| cm->DeleteAllAsync(CookieStore::DeleteCallback()); |
| auto one_more_cookie = CanonicalCookie::Create( |
| original_url_wo_cookie, "one_more_cookie=true", base::Time::Now(), |
| absl::nullopt /* server_time */, |
| absl::nullopt /* cookie_partition_key */); |
| cm->SetCanonicalCookieAsync(std::move(one_more_cookie), |
| original_url_wo_cookie, |
| net::CookieOptions::MakeAllInclusive(), |
| CookieStore::SetCookiesCallback()); |
| |
| request->FollowDeferredRedirect(absl::nullopt, absl::nullopt); |
| delegate.RunUntilComplete(); |
| EXPECT_THAT(delegate.request_status(), IsOk()); |
| |
| // There are DCHECKs in URLRequestHttpJob that would fail if |
| // maybe_sent_cookies and maybe_stored_cookies we not cleared properly. |
| |
| EXPECT_EQ(2, filtering_network_delegate.blocked_annotate_cookies_count()); |
| |
| // The number of cookies blocked from the most recent round trip. |
| ASSERT_EQ(1u, request->maybe_sent_cookies().size()); |
| EXPECT_EQ("one_more_cookie", |
| request->maybe_sent_cookies().front().cookie.Name()); |
| EXPECT_EQ("true", request->maybe_sent_cookies().front().cookie.Value()); |
| EXPECT_TRUE( |
| request->maybe_sent_cookies() |
| .front() |
| .access_result.status.HasExactlyExclusionReasonsForTesting( |
| {net::CookieInclusionStatus::EXCLUDE_USER_PREFERENCES})); |
| } |
| } |
| |
| TEST_F(URLRequestTestHTTP, RedirectPreserveFirstPartyURL) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| GURL url(http_test_server()->GetURL("/redirect302-to-echo")); |
| GURL first_party_url("http://example.com"); |
| |
| TestDelegate d; |
| std::unique_ptr<URLRequest> r(default_context().CreateRequest( |
| url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| r->set_site_for_cookies(SiteForCookies::FromUrl(first_party_url)); |
| |
| r->Start(); |
| d.RunUntilComplete(); |
| |
| EXPECT_EQ(2U, r->url_chain().size()); |
| EXPECT_EQ(OK, d.request_status()); |
| EXPECT_TRUE(SiteForCookies::FromUrl(first_party_url) |
| .IsEquivalent(r->site_for_cookies())); |
| } |
| |
| TEST_F(URLRequestTestHTTP, RedirectUpdateFirstPartyURL) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| GURL url(http_test_server()->GetURL("/redirect302-to-echo")); |
| GURL original_first_party_url("http://example.com"); |
| GURL expected_first_party_url(http_test_server()->GetURL("/echo")); |
| |
| TestDelegate d; |
| |
| std::unique_ptr<URLRequest> r(default_context().CreateRequest( |
| url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| r->set_site_for_cookies(SiteForCookies::FromUrl(original_first_party_url)); |
| r->set_first_party_url_policy( |
| RedirectInfo::FirstPartyURLPolicy::UPDATE_URL_ON_REDIRECT); |
| |
| r->Start(); |
| d.RunUntilComplete(); |
| |
| EXPECT_EQ(2U, r->url_chain().size()); |
| EXPECT_EQ(OK, d.request_status()); |
| EXPECT_TRUE(SiteForCookies::FromUrl(expected_first_party_url) |
| .IsEquivalent(r->site_for_cookies())); |
| } |
| |
| TEST_F(URLRequestTestHTTP, InterceptPost302RedirectGet) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| const char kData[] = "hello world"; |
| |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req(default_context().CreateRequest( |
| http_test_server()->GetURL("/defaultresponse"), DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->set_method("POST"); |
| req->set_upload(CreateSimpleUploadData(kData)); |
| HttpRequestHeaders headers; |
| headers.SetHeader(HttpRequestHeaders::kContentLength, |
| base::NumberToString(std::size(kData) - 1)); |
| req->SetExtraRequestHeaders(headers); |
| |
| std::unique_ptr<URLRequestRedirectJob> job = |
| std::make_unique<URLRequestRedirectJob>( |
| req.get(), http_test_server()->GetURL("/echo"), |
| RedirectUtil::ResponseCode::REDIRECT_302_FOUND, "Very Good Reason"); |
| TestScopedURLInterceptor interceptor(req->url(), std::move(job)); |
| |
| req->Start(); |
| d.RunUntilComplete(); |
| EXPECT_EQ("GET", req->method()); |
| } |
| |
| TEST_F(URLRequestTestHTTP, InterceptPost307RedirectPost) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| const char kData[] = "hello world"; |
| |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req(default_context().CreateRequest( |
| http_test_server()->GetURL("/defaultresponse"), DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->set_method("POST"); |
| req->set_upload(CreateSimpleUploadData(kData)); |
| HttpRequestHeaders headers; |
| headers.SetHeader(HttpRequestHeaders::kContentLength, |
| base::NumberToString(std::size(kData) - 1)); |
| req->SetExtraRequestHeaders(headers); |
| |
| std::unique_ptr<URLRequestRedirectJob> job = |
| std::make_unique<URLRequestRedirectJob>( |
| req.get(), http_test_server()->GetURL("/echo"), |
| RedirectUtil::ResponseCode::REDIRECT_307_TEMPORARY_REDIRECT, |
| "Very Good Reason"); |
| TestScopedURLInterceptor interceptor(req->url(), std::move(job)); |
| |
| req->Start(); |
| d.RunUntilComplete(); |
| EXPECT_EQ("POST", req->method()); |
| EXPECT_EQ(kData, d.data_received()); |
| } |
| |
| // Check that default A-L header is sent. |
| TEST_F(URLRequestTestHTTP, DefaultAcceptLanguage) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| auto context_builder = CreateTestURLRequestContextBuilder(); |
| context_builder->set_http_user_agent_settings( |
| std::make_unique<StaticHttpUserAgentSettings>("en", std::string())); |
| auto context = context_builder->Build(); |
| |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req(context->CreateRequest( |
| http_test_server()->GetURL("/echoheader?Accept-Language"), |
| DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->Start(); |
| d.RunUntilComplete(); |
| EXPECT_EQ("en", d.data_received()); |
| } |
| |
| // Check that an empty A-L header is not sent. http://crbug.com/77365. |
| TEST_F(URLRequestTestHTTP, EmptyAcceptLanguage) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| auto context_builder = CreateTestURLRequestContextBuilder(); |
| context_builder->set_http_user_agent_settings( |
| std::make_unique<StaticHttpUserAgentSettings>(std::string(), |
| std::string())); |
| auto context = context_builder->Build(); |
| |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req(context->CreateRequest( |
| http_test_server()->GetURL("/echoheader?Accept-Language"), |
| DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->Start(); |
| d.RunUntilComplete(); |
| EXPECT_EQ("None", d.data_received()); |
| } |
| |
| // Check that if request overrides the A-L header, the default is not appended. |
| // See http://crbug.com/20894 |
| TEST_F(URLRequestTestHTTP, OverrideAcceptLanguage) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req(default_context().CreateRequest( |
| http_test_server()->GetURL("/echoheader?Accept-Language"), |
| DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| HttpRequestHeaders headers; |
| headers.SetHeader(HttpRequestHeaders::kAcceptLanguage, "ru"); |
| req->SetExtraRequestHeaders(headers); |
| req->Start(); |
| d.RunUntilComplete(); |
| EXPECT_EQ(std::string("ru"), d.data_received()); |
| } |
| |
| // Check that default A-E header is sent. |
| TEST_F(URLRequestTestHTTP, DefaultAcceptEncoding) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req(default_context().CreateRequest( |
| http_test_server()->GetURL("/echoheader?Accept-Encoding"), |
| DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| HttpRequestHeaders headers; |
| req->SetExtraRequestHeaders(headers); |
| req->Start(); |
| d.RunUntilComplete(); |
| EXPECT_TRUE(ContainsString(d.data_received(), "gzip")); |
| } |
| |
| // Check that it's possible to override the default A-E header. |
| TEST_F(URLRequestTestHTTP, DefaultAcceptEncodingOverriden) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| struct { |
| base::flat_set<net::SourceStream::SourceType> accepted_types; |
| const char* expected_accept_encoding; |
| } tests[] = {{{net::SourceStream::SourceType::TYPE_DEFLATE}, "deflate"}, |
| {{}, "None"}, |
| {{net::SourceStream::SourceType::TYPE_GZIP}, "gzip"}, |
| {{net::SourceStream::SourceType::TYPE_GZIP, |
| net::SourceStream::SourceType::TYPE_DEFLATE}, |
| "gzip, deflate"}}; |
| for (auto test : tests) { |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req(default_context().CreateRequest( |
| http_test_server()->GetURL("/echoheader?Accept-Encoding"), |
| DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->set_accepted_stream_types(test.accepted_types); |
| req->Start(); |
| d.RunUntilComplete(); |
| EXPECT_STRCASEEQ(d.data_received().c_str(), test.expected_accept_encoding); |
| } |
| } |
| |
| // Check that if request overrides the A-E header, the default is not appended. |
| // See http://crbug.com/47381 |
| TEST_F(URLRequestTestHTTP, OverrideAcceptEncoding) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req(default_context().CreateRequest( |
| http_test_server()->GetURL("/echoheader?Accept-Encoding"), |
| DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| HttpRequestHeaders headers; |
| headers.SetHeader(HttpRequestHeaders::kAcceptEncoding, "identity"); |
| req->SetExtraRequestHeaders(headers); |
| req->Start(); |
| d.RunUntilComplete(); |
| EXPECT_FALSE(ContainsString(d.data_received(), "gzip")); |
| EXPECT_TRUE(ContainsString(d.data_received(), "identity")); |
| } |
| |
| // Check that setting the A-C header sends the proper header. |
| TEST_F(URLRequestTestHTTP, SetAcceptCharset) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req(default_context().CreateRequest( |
| http_test_server()->GetURL("/echoheader?Accept-Charset"), |
| DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| HttpRequestHeaders headers; |
| headers.SetHeader(HttpRequestHeaders::kAcceptCharset, "koi-8r"); |
| req->SetExtraRequestHeaders(headers); |
| req->Start(); |
| d.RunUntilComplete(); |
| EXPECT_EQ(std::string("koi-8r"), d.data_received()); |
| } |
| |
| // Check that default User-Agent header is sent. |
| TEST_F(URLRequestTestHTTP, DefaultUserAgent) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req(default_context().CreateRequest( |
| http_test_server()->GetURL("/echoheader?User-Agent"), DEFAULT_PRIORITY, |
| &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->Start(); |
| d.RunUntilComplete(); |
| EXPECT_EQ(default_context().http_user_agent_settings()->GetUserAgent(), |
| d.data_received()); |
| } |
| |
| // Check that if request overrides the User-Agent header, |
| // the default is not appended. |
| // TODO(crbug.com/564656) This test is flaky on iOS. |
| #if BUILDFLAG(IS_IOS) |
| #define MAYBE_OverrideUserAgent FLAKY_OverrideUserAgent |
| #else |
| #define MAYBE_OverrideUserAgent OverrideUserAgent |
| #endif |
| TEST_F(URLRequestTestHTTP, MAYBE_OverrideUserAgent) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req(default_context().CreateRequest( |
| http_test_server()->GetURL("/echoheader?User-Agent"), DEFAULT_PRIORITY, |
| &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| HttpRequestHeaders headers; |
| headers.SetHeader(HttpRequestHeaders::kUserAgent, "Lynx (textmode)"); |
| req->SetExtraRequestHeaders(headers); |
| req->Start(); |
| d.RunUntilComplete(); |
| EXPECT_EQ(std::string("Lynx (textmode)"), d.data_received()); |
| } |
| |
| // Check that a NULL HttpUserAgentSettings causes the corresponding empty |
| // User-Agent header to be sent but does not send the Accept-Language and |
| // Accept-Charset headers. |
| TEST_F(URLRequestTestHTTP, EmptyHttpUserAgentSettings) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| auto context_builder = CreateTestURLRequestContextBuilder(); |
| context_builder->set_http_user_agent_settings(nullptr); |
| auto context = context_builder->Build(); |
| |
| struct { |
| const char* request; |
| const char* expected_response; |
| } tests[] = {{"/echoheader?Accept-Language", "None"}, |
| {"/echoheader?Accept-Charset", "None"}, |
| {"/echoheader?User-Agent", ""}}; |
| |
| for (const auto& test : tests) { |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req(context->CreateRequest( |
| http_test_server()->GetURL(test.request), DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->Start(); |
| d.RunUntilComplete(); |
| EXPECT_EQ(test.expected_response, d.data_received()) |
| << " Request = \"" << test.request << "\""; |
| } |
| } |
| |
| // Make sure that URLRequest passes on its priority updates to |
| // newly-created jobs after the first one. |
| TEST_F(URLRequestTestHTTP, SetSubsequentJobPriority) { |
| GURL initial_url("http://foo.test/"); |
| GURL redirect_url("http://bar.test/"); |
| |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req(default_context().CreateRequest( |
| initial_url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| EXPECT_EQ(DEFAULT_PRIORITY, req->priority()); |
| |
| std::unique_ptr<URLRequestRedirectJob> redirect_job = |
| std::make_unique<URLRequestRedirectJob>( |
| req.get(), redirect_url, |
| RedirectUtil::ResponseCode::REDIRECT_302_FOUND, "Very Good Reason"); |
| auto interceptor = std::make_unique<TestScopedURLInterceptor>( |
| initial_url, std::move(redirect_job)); |
| |
| req->SetPriority(LOW); |
| req->Start(); |
| EXPECT_TRUE(req->is_pending()); |
| d.RunUntilRedirect(); |
| interceptor.reset(); |
| |
| RequestPriority job_priority; |
| std::unique_ptr<URLRequestJob> job = |
| std::make_unique<PriorityMonitoringURLRequestJob>(req.get(), |
| &job_priority); |
| interceptor = |
| std::make_unique<TestScopedURLInterceptor>(redirect_url, std::move(job)); |
| |
| // Should trigger |job| to be started. |
| req->FollowDeferredRedirect(absl::nullopt /* removed_headers */, |
| absl::nullopt /* modified_headers */); |
| d.RunUntilComplete(); |
| EXPECT_EQ(LOW, job_priority); |
| } |
| |
| // Check that creating a network request while entering/exiting suspend mode |
| // fails as it should. This is the only case where an HttpTransactionFactory |
| // does not return an HttpTransaction. |
| TEST_F(URLRequestTestHTTP, NetworkSuspendTest) { |
| auto context_builder = CreateTestURLRequestContextBuilder(); |
| context_builder->SetCreateHttpTransactionFactoryCallback( |
| base::BindOnce([](HttpNetworkSession* session) { |
| // Create a new HttpNetworkLayer that thinks it's suspended. |
| auto network_layer = std::make_unique<HttpNetworkLayer>(session); |
| network_layer->OnSuspend(); |
| std::unique_ptr<HttpTransactionFactory> factory = |
| std::make_unique<HttpCache>(std::move(network_layer), |
| HttpCache::DefaultBackend::InMemory(0)); |
| return factory; |
| })); |
| auto context = context_builder->Build(); |
| |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req( |
| context->CreateRequest(GURL("http://127.0.0.1/"), DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->Start(); |
| d.RunUntilComplete(); |
| |
| EXPECT_TRUE(d.request_failed()); |
| EXPECT_EQ(ERR_NETWORK_IO_SUSPENDED, d.request_status()); |
| } |
| |
| namespace { |
| |
| // HttpTransactionFactory that synchronously fails to create transactions. |
| class FailingHttpTransactionFactory : public HttpTransactionFactory { |
| public: |
| explicit FailingHttpTransactionFactory(HttpNetworkSession* network_session) |
| : network_session_(network_session) {} |
| |
| FailingHttpTransactionFactory(const FailingHttpTransactionFactory&) = delete; |
| FailingHttpTransactionFactory& operator=( |
| const FailingHttpTransactionFactory&) = delete; |
| |
| ~FailingHttpTransactionFactory() override = default; |
| |
| // HttpTransactionFactory methods: |
| int CreateTransaction(RequestPriority priority, |
| std::unique_ptr<HttpTransaction>* trans) override { |
| return ERR_FAILED; |
| } |
| |
| HttpCache* GetCache() override { return nullptr; } |
| |
| HttpNetworkSession* GetSession() override { return network_session_; } |
| |
| private: |
| raw_ptr<HttpNetworkSession> network_session_; |
| }; |
| |
| } // namespace |
| |
| // Check that when a request that fails to create an HttpTransaction can be |
| // cancelled while the failure notification is pending, and doesn't send two |
| // failure notifications. |
| // |
| // This currently only happens when in suspend mode and there's no cache, but |
| // just use a special HttpTransactionFactory, to avoid depending on those |
| // behaviors. |
| // |
| // Flaky crash: https://crbug.com/1348418 |
| #if BUILDFLAG(IS_CHROMEOS) |
| #define MAYBE_NetworkCancelAfterCreateTransactionFailsTest \ |
| DISABLED_NetworkCancelAfterCreateTransactionFailsTest |
| #else |
| #define MAYBE_NetworkCancelAfterCreateTransactionFailsTest \ |
| NetworkCancelAfterCreateTransactionFailsTest |
| #endif |
| TEST_F(URLRequestTestHTTP, MAYBE_NetworkCancelAfterCreateTransactionFailsTest) { |
| auto context_builder = CreateTestURLRequestContextBuilder(); |
| context_builder->SetCreateHttpTransactionFactoryCallback( |
| base::BindOnce([](HttpNetworkSession* session) { |
| std::unique_ptr<HttpTransactionFactory> factory = |
| std::make_unique<FailingHttpTransactionFactory>(session); |
| return factory; |
| })); |
| auto& network_delegate = *context_builder->set_network_delegate( |
| std::make_unique<TestNetworkDelegate>()); |
| auto context = context_builder->Build(); |
| |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req( |
| context->CreateRequest(GURL("http://127.0.0.1/"), DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| // Don't send cookies (Collecting cookies is asynchronous, and need request to |
| // try to create an HttpNetworkTransaction synchronously on start). |
| req->set_allow_credentials(false); |
| req->Start(); |
| req->Cancel(); |
| d.RunUntilComplete(); |
| // Run pending error task, if there is one. |
| base::RunLoop().RunUntilIdle(); |
| |
| EXPECT_TRUE(d.request_failed()); |
| EXPECT_EQ(1, d.response_started_count()); |
| EXPECT_EQ(ERR_ABORTED, d.request_status()); |
| |
| // NetworkDelegate should see the cancellation, but not the error. |
| EXPECT_EQ(1, network_delegate.canceled_requests()); |
| EXPECT_EQ(0, network_delegate.error_count()); |
| } |
| |
| TEST_F(URLRequestTestHTTP, NetworkAccessedSetOnNetworkRequest) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| TestDelegate d; |
| GURL test_url(http_test_server()->GetURL("/")); |
| std::unique_ptr<URLRequest> req(default_context().CreateRequest( |
| test_url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| |
| req->Start(); |
| d.RunUntilComplete(); |
| |
| EXPECT_TRUE(req->response_info().network_accessed); |
| } |
| |
| TEST_F(URLRequestTestHTTP, NetworkAccessedClearOnCachedResponse) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| // Populate the cache. |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req(default_context().CreateRequest( |
| http_test_server()->GetURL("/cachetime"), DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->set_isolation_info(isolation_info1_); |
| req->Start(); |
| d.RunUntilComplete(); |
| |
| EXPECT_EQ(OK, d.request_status()); |
| EXPECT_TRUE(req->response_info().network_accessed); |
| EXPECT_FALSE(req->response_info().was_cached); |
| |
| req = default_context().CreateRequest( |
| http_test_server()->GetURL("/cachetime"), DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS); |
| req->set_isolation_info(isolation_info1_); |
| req->Start(); |
| d.RunUntilComplete(); |
| |
| EXPECT_EQ(OK, d.request_status()); |
| EXPECT_FALSE(req->response_info().network_accessed); |
| EXPECT_TRUE(req->response_info().was_cached); |
| } |
| |
| TEST_F(URLRequestTestHTTP, NetworkAccessedClearOnLoadOnlyFromCache) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| TestDelegate d; |
| GURL test_url(http_test_server()->GetURL("/")); |
| std::unique_ptr<URLRequest> req(default_context().CreateRequest( |
| test_url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->SetLoadFlags(LOAD_ONLY_FROM_CACHE | LOAD_SKIP_CACHE_VALIDATION); |
| |
| req->Start(); |
| d.RunUntilComplete(); |
| |
| EXPECT_FALSE(req->response_info().network_accessed); |
| } |
| |
| // Test that a single job with a THROTTLED priority completes |
| // correctly in the absence of contention. |
| TEST_F(URLRequestTestHTTP, ThrottledPriority) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| TestDelegate d; |
| GURL test_url(http_test_server()->GetURL("/")); |
| std::unique_ptr<URLRequest> req(default_context().CreateRequest( |
| test_url, THROTTLED, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->Start(); |
| d.RunUntilComplete(); |
| |
| EXPECT_EQ(OK, d.request_status()); |
| } |
| |
| TEST_F(URLRequestTestHTTP, RawBodyBytesNoContentEncoding) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req(default_context().CreateRequest( |
| http_test_server()->GetURL("/simple.html"), DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->Start(); |
| d.RunUntilComplete(); |
| |
| EXPECT_EQ(5, req->GetRawBodyBytes()); |
| } |
| |
| TEST_F(URLRequestTestHTTP, RawBodyBytesGzipEncoding) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req(default_context().CreateRequest( |
| http_test_server()->GetURL("/gzip-encoded"), DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->Start(); |
| d.RunUntilComplete(); |
| |
| EXPECT_EQ(30, req->GetRawBodyBytes()); |
| } |
| |
| // Check that if NetworkDelegate::OnBeforeStartTransaction returns an error, |
| // the delegate isn't called back synchronously. |
| TEST_F(URLRequestTestHTTP, TesBeforeStartTransactionFails) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| default_network_delegate().set_before_start_transaction_fails(); |
| |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req(default_context().CreateRequest( |
| http_test_server()->GetURL("/"), DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->Start(); |
| DCHECK(!d.response_completed()); |
| d.RunUntilComplete(); |
| DCHECK(d.response_completed()); |
| EXPECT_EQ(ERR_FAILED, d.request_status()); |
| } |
| |
| class URLRequestTestReferrerPolicy : public URLRequestTest { |
| public: |
| URLRequestTestReferrerPolicy() = default; |
| |
| void InstantiateSameOriginServers(net::EmbeddedTestServer::Type type) { |
| origin_server_ = std::make_unique<EmbeddedTestServer>(type); |
| RegisterDefaultHandlers(origin_server_.get()); |
| ASSERT_TRUE(origin_server_->Start()); |
| } |
| |
| void InstantiateCrossOriginServers(net::EmbeddedTestServer::Type origin_type, |
| net::EmbeddedTestServer::Type dest_type) { |
| origin_server_ = std::make_unique<EmbeddedTestServer>(origin_type); |
| RegisterDefaultHandlers(origin_server_.get()); |
| ASSERT_TRUE(origin_server_->Start()); |
| |
| destination_server_ = std::make_unique<EmbeddedTestServer>(dest_type); |
| RegisterDefaultHandlers(destination_server_.get()); |
| ASSERT_TRUE(destination_server_->Start()); |
| } |
| |
| void VerifyReferrerAfterRedirect(ReferrerPolicy policy, |
| const GURL& referrer, |
| const GURL& expected) { |
| // Create and execute the request: we'll only have a |destination_server_| |
| // if the origins are meant to be distinct. Otherwise, we'll use the |
| // |origin_server_| for both endpoints. |
| GURL destination_url = |
| destination_server_ ? destination_server_->GetURL("/echoheader?Referer") |
| : origin_server_->GetURL("/echoheader?Referer"); |
| GURL origin_url = |
| origin_server_->GetURL("/server-redirect?" + destination_url.spec()); |
| |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req(default_context().CreateRequest( |
| origin_url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->set_referrer_policy(policy); |
| req->SetReferrer(referrer.spec()); |
| req->Start(); |
| d.RunUntilComplete(); |
| |
| EXPECT_EQ(1, d.response_started_count()); |
| EXPECT_EQ(1, d.received_redirect_count()); |
| EXPECT_EQ(destination_url, req->url()); |
| EXPECT_EQ(OK, d.request_status()); |
| EXPECT_EQ(200, req->response_headers()->response_code()); |
| |
| EXPECT_EQ(expected.spec(), req->referrer()); |
| if (expected.is_empty()) |
| EXPECT_EQ("None", d.data_received()); |
| else |
| EXPECT_EQ(expected.spec(), d.data_received()); |
| } |
| |
| EmbeddedTestServer* origin_server() const { return origin_server_.get(); } |
| |
| private: |
| std::unique_ptr<EmbeddedTestServer> origin_server_; |
| std::unique_ptr<EmbeddedTestServer> destination_server_; |
| }; |
| |
| TEST_F(URLRequestTestReferrerPolicy, HTTPToSameOriginHTTP) { |
| InstantiateSameOriginServers(net::EmbeddedTestServer::TYPE_HTTP); |
| |
| GURL referrer = origin_server()->GetURL("/path/to/file.html"); |
| VerifyReferrerAfterRedirect( |
| ReferrerPolicy::CLEAR_ON_TRANSITION_FROM_SECURE_TO_INSECURE, referrer, |
| referrer); |
| |
| VerifyReferrerAfterRedirect( |
| ReferrerPolicy::REDUCE_GRANULARITY_ON_TRANSITION_CROSS_ORIGIN, referrer, |
| referrer); |
| |
| VerifyReferrerAfterRedirect( |
| ReferrerPolicy::ORIGIN_ONLY_ON_TRANSITION_CROSS_ORIGIN, referrer, |
| referrer); |
| |
| VerifyReferrerAfterRedirect(ReferrerPolicy::NEVER_CLEAR, referrer, referrer); |
| |
| // The original referrer set on the request is expected to obey the referrer |
| // policy and already be stripped to the origin; thus this test case just |
| // checks that this policy doesn't cause the referrer to change when following |
| // a redirect. |
| VerifyReferrerAfterRedirect(ReferrerPolicy::ORIGIN, |
| referrer.DeprecatedGetOriginAsURL(), |
| referrer.DeprecatedGetOriginAsURL()); |
| |
| VerifyReferrerAfterRedirect(ReferrerPolicy::CLEAR_ON_TRANSITION_CROSS_ORIGIN, |
| referrer, referrer); |
| |
| // The original referrer set on the request is expected to obey the referrer |
| // policy and already be stripped to the origin; thus this test case just |
| // checks that this policy doesn't cause the referrer to change when following |
| // a redirect. |
| VerifyReferrerAfterRedirect( |
| ReferrerPolicy::ORIGIN_CLEAR_ON_TRANSITION_FROM_SECURE_TO_INSECURE, |
| referrer.DeprecatedGetOriginAsURL(), referrer.DeprecatedGetOriginAsURL()); |
| |
| VerifyReferrerAfterRedirect(ReferrerPolicy::NO_REFERRER, GURL(), GURL()); |
| } |
| |
| TEST_F(URLRequestTestReferrerPolicy, HTTPToCrossOriginHTTP) { |
| InstantiateCrossOriginServers(net::EmbeddedTestServer::TYPE_HTTP, |
| net::EmbeddedTestServer::TYPE_HTTP); |
| GURL referrer = origin_server()->GetURL("/path/to/file.html"); |
| |
| VerifyReferrerAfterRedirect( |
| ReferrerPolicy::CLEAR_ON_TRANSITION_FROM_SECURE_TO_INSECURE, referrer, |
| referrer); |
| |
| VerifyReferrerAfterRedirect( |
| ReferrerPolicy::REDUCE_GRANULARITY_ON_TRANSITION_CROSS_ORIGIN, referrer, |
| referrer.DeprecatedGetOriginAsURL()); |
| |
| VerifyReferrerAfterRedirect( |
| ReferrerPolicy::ORIGIN_ONLY_ON_TRANSITION_CROSS_ORIGIN, referrer, |
| referrer.DeprecatedGetOriginAsURL()); |
| |
| VerifyReferrerAfterRedirect(ReferrerPolicy::NEVER_CLEAR, referrer, referrer); |
| |
| // The original referrer set on the request is expected to obey the referrer |
| // policy and already be stripped to the origin; thus this test case just |
| // checks that this policy doesn't cause the referrer to change when following |
| // a redirect. |
| VerifyReferrerAfterRedirect(ReferrerPolicy::ORIGIN, |
| referrer.DeprecatedGetOriginAsURL(), |
| referrer.DeprecatedGetOriginAsURL()); |
| |
| VerifyReferrerAfterRedirect(ReferrerPolicy::CLEAR_ON_TRANSITION_CROSS_ORIGIN, |
| referrer, GURL()); |
| |
| // The original referrer set on the request is expected to obey the referrer |
| // policy and already be stripped to the origin; thus this test case just |
| // checks that this policy doesn't cause the referrer to change when following |
| // a redirect. |
| VerifyReferrerAfterRedirect( |
| ReferrerPolicy::ORIGIN_CLEAR_ON_TRANSITION_FROM_SECURE_TO_INSECURE, |
| referrer.DeprecatedGetOriginAsURL(), referrer.DeprecatedGetOriginAsURL()); |
| |
| VerifyReferrerAfterRedirect(ReferrerPolicy::NO_REFERRER, GURL(), GURL()); |
| } |
| |
| TEST_F(URLRequestTestReferrerPolicy, HTTPSToSameOriginHTTPS) { |
| InstantiateSameOriginServers(net::EmbeddedTestServer::TYPE_HTTPS); |
| GURL referrer = origin_server()->GetURL("/path/to/file.html"); |
| |
| VerifyReferrerAfterRedirect( |
| ReferrerPolicy::CLEAR_ON_TRANSITION_FROM_SECURE_TO_INSECURE, referrer, |
| referrer); |
| |
| VerifyReferrerAfterRedirect( |
| ReferrerPolicy::REDUCE_GRANULARITY_ON_TRANSITION_CROSS_ORIGIN, referrer, |
| referrer); |
| |
| VerifyReferrerAfterRedirect( |
| ReferrerPolicy::ORIGIN_ONLY_ON_TRANSITION_CROSS_ORIGIN, referrer, |
| referrer); |
| |
| VerifyReferrerAfterRedirect(ReferrerPolicy::NEVER_CLEAR, referrer, referrer); |
| |
| // The original referrer set on the request is expected to obey the referrer |
| // policy and already be stripped to the origin; thus this test case just |
| // checks that this policy doesn't cause the referrer to change when following |
| // a redirect. |
| VerifyReferrerAfterRedirect(ReferrerPolicy::ORIGIN, |
| referrer.DeprecatedGetOriginAsURL(), |
| referrer.DeprecatedGetOriginAsURL()); |
| |
| VerifyReferrerAfterRedirect(ReferrerPolicy::CLEAR_ON_TRANSITION_CROSS_ORIGIN, |
| referrer, referrer); |
| |
| // The original referrer set on the request is expected to obey the referrer |
| // policy and already be stripped to the origin; thus this test case just |
| // checks that this policy doesn't cause the referrer to change when following |
| // a redirect. |
| VerifyReferrerAfterRedirect( |
| ReferrerPolicy::ORIGIN_CLEAR_ON_TRANSITION_FROM_SECURE_TO_INSECURE, |
| referrer.DeprecatedGetOriginAsURL(), referrer.DeprecatedGetOriginAsURL()); |
| |
| VerifyReferrerAfterRedirect(ReferrerPolicy::NO_REFERRER, GURL(), GURL()); |
| } |
| |
| TEST_F(URLRequestTestReferrerPolicy, HTTPSToCrossOriginHTTPS) { |
| InstantiateCrossOriginServers(net::EmbeddedTestServer::TYPE_HTTPS, |
| net::EmbeddedTestServer::TYPE_HTTPS); |
| GURL referrer = origin_server()->GetURL("/path/to/file.html"); |
| |
| VerifyReferrerAfterRedirect( |
| ReferrerPolicy::CLEAR_ON_TRANSITION_FROM_SECURE_TO_INSECURE, referrer, |
| referrer); |
| |
| VerifyReferrerAfterRedirect( |
| ReferrerPolicy::REDUCE_GRANULARITY_ON_TRANSITION_CROSS_ORIGIN, referrer, |
| origin_server()->GetURL("/")); |
| |
| VerifyReferrerAfterRedirect( |
| ReferrerPolicy::ORIGIN_ONLY_ON_TRANSITION_CROSS_ORIGIN, referrer, |
| origin_server()->GetURL("/")); |
| |
| VerifyReferrerAfterRedirect(ReferrerPolicy::NEVER_CLEAR, referrer, referrer); |
| |
| // The original referrer set on the request is expected to obey the referrer |
| // policy and already be stripped to the origin; thus this test case just |
| // checks that this policy doesn't cause the referrer to change when following |
| // a redirect. |
| VerifyReferrerAfterRedirect(ReferrerPolicy::ORIGIN, |
| referrer.DeprecatedGetOriginAsURL(), |
| referrer.DeprecatedGetOriginAsURL()); |
| |
| VerifyReferrerAfterRedirect(ReferrerPolicy::CLEAR_ON_TRANSITION_CROSS_ORIGIN, |
| referrer, GURL()); |
| |
| // The original referrer set on the request is expected to obey the referrer |
| // policy and already be stripped to the origin; thus this test case just |
| // checks that this policy doesn't cause the referrer to change when following |
| // a redirect. |
| VerifyReferrerAfterRedirect( |
| ReferrerPolicy::ORIGIN_CLEAR_ON_TRANSITION_FROM_SECURE_TO_INSECURE, |
| referrer.DeprecatedGetOriginAsURL(), referrer.DeprecatedGetOriginAsURL()); |
| |
| VerifyReferrerAfterRedirect(ReferrerPolicy::NO_REFERRER, GURL(), GURL()); |
| } |
| |
| TEST_F(URLRequestTestReferrerPolicy, HTTPToHTTPS) { |
| InstantiateCrossOriginServers(net::EmbeddedTestServer::TYPE_HTTP, |
| net::EmbeddedTestServer::TYPE_HTTPS); |
| GURL referrer = origin_server()->GetURL("/path/to/file.html"); |
| |
| VerifyReferrerAfterRedirect( |
| ReferrerPolicy::CLEAR_ON_TRANSITION_FROM_SECURE_TO_INSECURE, referrer, |
| referrer); |
| |
| VerifyReferrerAfterRedirect( |
| ReferrerPolicy::REDUCE_GRANULARITY_ON_TRANSITION_CROSS_ORIGIN, referrer, |
| origin_server()->GetURL("/")); |
| |
| VerifyReferrerAfterRedirect( |
| ReferrerPolicy::ORIGIN_ONLY_ON_TRANSITION_CROSS_ORIGIN, referrer, |
| origin_server()->GetURL("/")); |
| |
| VerifyReferrerAfterRedirect(ReferrerPolicy::NEVER_CLEAR, referrer, referrer); |
| |
| // The original referrer set on the request is expected to obey the referrer |
| // policy and already be stripped to the origin; thus this test case just |
| // checks that this policy doesn't cause the referrer to change when following |
| // a redirect. |
| VerifyReferrerAfterRedirect(ReferrerPolicy::ORIGIN, |
| referrer.DeprecatedGetOriginAsURL(), |
| referrer.DeprecatedGetOriginAsURL()); |
| |
| VerifyReferrerAfterRedirect(ReferrerPolicy::CLEAR_ON_TRANSITION_CROSS_ORIGIN, |
| referrer, GURL()); |
| |
| // The original referrer set on the request is expected to obey the referrer |
| // policy and already be stripped to the origin; thus this test case just |
| // checks that this policy doesn't cause the referrer to change when following |
| // a redirect. |
| VerifyReferrerAfterRedirect( |
| ReferrerPolicy::ORIGIN_CLEAR_ON_TRANSITION_FROM_SECURE_TO_INSECURE, |
| referrer.DeprecatedGetOriginAsURL(), referrer.DeprecatedGetOriginAsURL()); |
| |
| VerifyReferrerAfterRedirect(ReferrerPolicy::NO_REFERRER, GURL(), GURL()); |
| } |
| |
| TEST_F(URLRequestTestReferrerPolicy, HTTPSToHTTP) { |
| InstantiateCrossOriginServers(net::EmbeddedTestServer::TYPE_HTTPS, |
| net::EmbeddedTestServer::TYPE_HTTP); |
| GURL referrer = origin_server()->GetURL("/path/to/file.html"); |
| |
| VerifyReferrerAfterRedirect( |
| ReferrerPolicy::CLEAR_ON_TRANSITION_FROM_SECURE_TO_INSECURE, referrer, |
| GURL()); |
| |
| VerifyReferrerAfterRedirect( |
| ReferrerPolicy::REDUCE_GRANULARITY_ON_TRANSITION_CROSS_ORIGIN, referrer, |
| GURL()); |
| |
| VerifyReferrerAfterRedirect( |
| ReferrerPolicy::ORIGIN_ONLY_ON_TRANSITION_CROSS_ORIGIN, referrer, |
| origin_server()->GetURL("/")); |
| |
| VerifyReferrerAfterRedirect(ReferrerPolicy::NEVER_CLEAR, referrer, referrer); |
| |
| // The original referrer set on the request is expected to obey the referrer |
| // policy and already be stripped to the origin; thus this test case just |
| // checks that this policy doesn't cause the referrer to change when following |
| // a redirect. |
| VerifyReferrerAfterRedirect(ReferrerPolicy::ORIGIN, |
| referrer.DeprecatedGetOriginAsURL(), |
| referrer.DeprecatedGetOriginAsURL()); |
| |
| VerifyReferrerAfterRedirect(ReferrerPolicy::CLEAR_ON_TRANSITION_CROSS_ORIGIN, |
| referrer, GURL()); |
| |
| // The original referrer set on the request is expected to obey the referrer |
| // policy and already be stripped to the origin, though it should be |
| // subsequently cleared during the downgrading redirect. |
| VerifyReferrerAfterRedirect( |
| ReferrerPolicy::ORIGIN_CLEAR_ON_TRANSITION_FROM_SECURE_TO_INSECURE, |
| referrer.DeprecatedGetOriginAsURL(), GURL()); |
| |
| VerifyReferrerAfterRedirect(ReferrerPolicy::NO_REFERRER, GURL(), GURL()); |
| } |
| |
| class HTTPSRequestTest : public TestWithTaskEnvironment { |
| public: |
| HTTPSRequestTest() { |
| auto context_builder = CreateTestURLRequestContextBuilder(); |
| default_context_ = context_builder->Build(); |
| } |
| ~HTTPSRequestTest() override { |
| SetTransportSecurityStateSourceForTesting(nullptr); |
| } |
| |
| URLRequestContext& default_context() { return *default_context_; } |
| |
| private: |
| std::unique_ptr<URLRequestContext> default_context_; |
| }; |
| |
| TEST_F(HTTPSRequestTest, HTTPSGetTest) { |
| EmbeddedTestServer test_server(net::EmbeddedTestServer::TYPE_HTTPS); |
| RegisterDefaultHandlers(&test_server); |
| ASSERT_TRUE(test_server.Start()); |
| |
| TestDelegate d; |
| { |
| std::unique_ptr<URLRequest> r(default_context().CreateRequest( |
| test_server.GetURL("/defaultresponse"), DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| r->Start(); |
| EXPECT_TRUE(r->is_pending()); |
| |
| d.RunUntilComplete(); |
| |
| EXPECT_EQ(1, d.response_started_count()); |
| EXPECT_FALSE(d.received_data_before_response()); |
| EXPECT_NE(0, d.bytes_received()); |
| CheckSSLInfo(r->ssl_info()); |
| EXPECT_EQ(test_server.host_port_pair().host(), |
| r->GetResponseRemoteEndpoint().ToStringWithoutPort()); |
| EXPECT_EQ(test_server.host_port_pair().port(), |
| r->GetResponseRemoteEndpoint().port()); |
| } |
| } |
| |
| TEST_F(HTTPSRequestTest, HTTPSMismatchedTest) { |
| EmbeddedTestServer test_server(net::EmbeddedTestServer::TYPE_HTTPS); |
| test_server.SetSSLConfig(net::EmbeddedTestServer::CERT_MISMATCHED_NAME); |
| RegisterDefaultHandlers(&test_server); |
| ASSERT_TRUE(test_server.Start()); |
| |
| bool err_allowed = true; |
| for (int i = 0; i < 2; i++, err_allowed = !err_allowed) { |
| TestDelegate d; |
| { |
| d.set_allow_certificate_errors(err_allowed); |
| std::unique_ptr<URLRequest> r(default_context().CreateRequest( |
| test_server.GetURL("/defaultresponse"), DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| |
| r->Start(); |
| EXPECT_TRUE(r->is_pending()); |
| |
| d.RunUntilComplete(); |
| |
| EXPECT_EQ(1, d.response_started_count()); |
| EXPECT_FALSE(d.received_data_before_response()); |
| EXPECT_TRUE(d.have_certificate_errors()); |
| if (err_allowed) { |
| EXPECT_NE(0, d.bytes_received()); |
| CheckSSLInfo(r->ssl_info()); |
| } else { |
| EXPECT_EQ(0, d.bytes_received()); |
| } |
| } |
| } |
| } |
| |
| TEST_F(HTTPSRequestTest, HTTPSExpiredTest) { |
| EmbeddedTestServer test_server(net::EmbeddedTestServer::TYPE_HTTPS); |
| test_server.SetSSLConfig(net::EmbeddedTestServer::CERT_EXPIRED); |
| RegisterDefaultHandlers(&test_server); |
| ASSERT_TRUE(test_server.Start()); |
| |
| // Iterate from false to true, just so that we do the opposite of the |
| // previous test in order to increase test coverage. |
| bool err_allowed = false; |
| for (int i = 0; i < 2; i++, err_allowed = !err_allowed) { |
| TestDelegate d; |
| { |
| d.set_allow_certificate_errors(err_allowed); |
| std::unique_ptr<URLRequest> r(default_context().CreateRequest( |
| test_server.GetURL("/defaultresponse"), DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| |
| r->Start(); |
| EXPECT_TRUE(r->is_pending()); |
| |
| d.RunUntilComplete(); |
| |
| EXPECT_EQ(1, d.response_started_count()); |
| EXPECT_FALSE(d.received_data_before_response()); |
| EXPECT_TRUE(d.have_certificate_errors()); |
| if (err_allowed) { |
| EXPECT_NE(0, d.bytes_received()); |
| CheckSSLInfo(r->ssl_info()); |
| } else { |
| EXPECT_EQ(0, d.bytes_received()); |
| } |
| } |
| } |
| } |
| |
| // A TestDelegate used to test that an appropriate net error code is provided |
| // when an SSL certificate error occurs. |
| class SSLNetErrorTestDelegate : public TestDelegate { |
| public: |
| void OnSSLCertificateError(URLRequest* request, |
| int net_error, |
| const SSLInfo& ssl_info, |
| bool fatal) override { |
| net_error_ = net_error; |
| on_ssl_certificate_error_called_ = true; |
| TestDelegate::OnSSLCertificateError(request, net_error, ssl_info, fatal); |
| } |
| |
| bool on_ssl_certificate_error_called() { |
| return on_ssl_certificate_error_called_; |
| } |
| |
| int net_error() { return net_error_; } |
| |
| private: |
| bool on_ssl_certificate_error_called_ = false; |
| int net_error_ = net::OK; |
| }; |
| |
| // Tests that the URLRequest::Delegate receives an appropriate net error code |
| // when an SSL certificate error occurs. |
| TEST_F(HTTPSRequestTest, SSLNetErrorReportedToDelegate) { |
| EmbeddedTestServer test_server(net::EmbeddedTestServer::TYPE_HTTPS); |
| test_server.SetSSLConfig(net::EmbeddedTestServer::CERT_EXPIRED); |
| RegisterDefaultHandlers(&test_server); |
| ASSERT_TRUE(test_server.Start()); |
| |
| SSLNetErrorTestDelegate d; |
| std::unique_ptr<URLRequest> r(default_context().CreateRequest( |
| test_server.GetURL("/defaultresponse"), DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| r->Start(); |
| EXPECT_TRUE(r->is_pending()); |
| d.RunUntilComplete(); |
| |
| EXPECT_TRUE(d.on_ssl_certificate_error_called()); |
| EXPECT_EQ(net::ERR_CERT_DATE_INVALID, d.net_error()); |
| } |
| |
| // TODO(svaldez): iOS tests are flaky with EmbeddedTestServer and transport |
| // security state. (see http://crbug.com/550977). |
| #if !BUILDFLAG(IS_IOS) |
| // This tests that a load of a domain with preloaded HSTS and HPKP with a |
| // certificate error sets the |certificate_errors_are_fatal| flag correctly. |
| // This flag will cause the interstitial to be fatal. |
| TEST_F(HTTPSRequestTest, HTTPSPreloadedHSTSTest) { |
| SetTransportSecurityStateSourceForTesting(&test_default::kHSTSSource); |
| |
| EmbeddedTestServer test_server(net::EmbeddedTestServer::TYPE_HTTPS); |
| test_server.SetSSLConfig(net::EmbeddedTestServer::CERT_MISMATCHED_NAME); |
| test_server.ServeFilesFromSourceDirectory("net/data/ssl"); |
| ASSERT_TRUE(test_server.Start()); |
| |
| // We require that the URL be hsts-hpkp-preloaded.test. This is a test domain |
| // that has a preloaded HSTS+HPKP entry in the TransportSecurityState. This |
| // means that we have to use a MockHostResolver in order to direct |
| // hsts-hpkp-preloaded.test to the testserver. |
| |
| auto host_resolver = std::make_unique<MockHostResolver>(); |
| host_resolver->rules()->AddRule("hsts-hpkp-preloaded.test", |
| test_server.GetIPLiteralString()); |
| auto context_builder = CreateTestURLRequestContextBuilder(); |
| context_builder->set_host_resolver(std::move(host_resolver)); |
| auto context = context_builder->Build(); |
| |
| TestDelegate d; |
| std::unique_ptr<URLRequest> r(context->CreateRequest( |
| GURL(base::StringPrintf("https://hsts-hpkp-preloaded.test:%d", |
| test_server.host_port_pair().port())), |
| DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| |
| r->Start(); |
| EXPECT_TRUE(r->is_pending()); |
| |
| d.RunUntilComplete(); |
| |
| EXPECT_EQ(1, d.response_started_count()); |
| EXPECT_FALSE(d.received_data_before_response()); |
| EXPECT_TRUE(d.have_certificate_errors()); |
| EXPECT_TRUE(d.certificate_errors_are_fatal()); |
| } |
| |
| // This tests that cached HTTPS page loads do not cause any updates to the |
| // TransportSecurityState. |
| TEST_F(HTTPSRequestTest, HTTPSErrorsNoClobberTSSTest) { |
| base::test::ScopedFeatureList scoped_feature_list_; |
| scoped_feature_list_.InitAndEnableFeature( |
| net::features::kStaticKeyPinningEnforcement); |
| SetTransportSecurityStateSourceForTesting(&test_default::kHSTSSource); |
| |
| // The actual problem -- CERT_MISMATCHED_NAME in this case -- doesn't |
| // matter. It just has to be any error. |
| EmbeddedTestServer test_server(net::EmbeddedTestServer::TYPE_HTTPS); |
| test_server.SetSSLConfig(net::EmbeddedTestServer::CERT_MISMATCHED_NAME); |
| test_server.ServeFilesFromSourceDirectory("net/data/ssl"); |
| ASSERT_TRUE(test_server.Start()); |
| |
| // We require that the URL be hsts-hpkp-preloaded.test. This is a test domain |
| // that has a preloaded HSTS+HPKP entry in the TransportSecurityState. This |
| // means that we have to use a MockHostResolver in order to direct |
| // hsts-hpkp-preloaded.test to the testserver. |
| |
| auto host_resolver = std::make_unique<MockHostResolver>(); |
| host_resolver->rules()->AddRule("hsts-hpkp-preloaded.test", |
| test_server.GetIPLiteralString()); |
| auto context_builder = CreateTestURLRequestContextBuilder(); |
| context_builder->set_host_resolver(std::move(host_resolver)); |
| auto context = context_builder->Build(); |
| ASSERT_TRUE(context->transport_security_state()); |
| TransportSecurityState& transport_security_state = |
| *context->transport_security_state(); |
| |
| transport_security_state.EnableStaticPinsForTesting(); |
| transport_security_state.SetPinningListAlwaysTimelyForTesting(true); |
| |
| TransportSecurityState::STSState static_sts_state; |
| TransportSecurityState::PKPState static_pkp_state; |
| EXPECT_TRUE(transport_security_state.GetStaticSTSState( |
| "hsts-hpkp-preloaded.test", &static_sts_state)); |
| EXPECT_TRUE(transport_security_state.GetStaticPKPState( |
| "hsts-hpkp-preloaded.test", &static_pkp_state)); |
| |
| TransportSecurityState::STSState dynamic_sts_state; |
| TransportSecurityState::PKPState dynamic_pkp_state; |
| EXPECT_FALSE(transport_security_state.GetDynamicSTSState( |
| "hsts-hpkp-preloaded.test", &dynamic_sts_state)); |
| EXPECT_FALSE(transport_security_state.GetDynamicPKPState( |
| "hsts-hpkp-preloaded.test", &dynamic_pkp_state)); |
| |
| TestDelegate d; |
| std::unique_ptr<URLRequest> r(context->CreateRequest( |
| GURL(base::StringPrintf("https://hsts-hpkp-preloaded.test:%d", |
| test_server.host_port_pair().port())), |
| DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| |
| r->Start(); |
| EXPECT_TRUE(r->is_pending()); |
| |
| d.RunUntilComplete(); |
| |
| EXPECT_EQ(1, d.response_started_count()); |
| EXPECT_FALSE(d.received_data_before_response()); |
| EXPECT_TRUE(d.have_certificate_errors()); |
| EXPECT_TRUE(d.certificate_errors_are_fatal()); |
| |
| // Get a fresh copy of the states, and check that they haven't changed. |
| TransportSecurityState::STSState new_static_sts_state; |
| TransportSecurityState::PKPState new_static_pkp_state; |
| EXPECT_TRUE(transport_security_state.GetStaticSTSState( |
| "hsts-hpkp-preloaded.test", &new_static_sts_state)); |
| EXPECT_TRUE(transport_security_state.GetStaticPKPState( |
| "hsts-hpkp-preloaded.test", &new_static_pkp_state)); |
| TransportSecurityState::STSState new_dynamic_sts_state; |
| TransportSecurityState::PKPState new_dynamic_pkp_state; |
| EXPECT_FALSE(transport_security_state.GetDynamicSTSState( |
| "hsts-hpkp-preloaded.test", &new_dynamic_sts_state)); |
| EXPECT_FALSE(transport_security_state.GetDynamicPKPState( |
| "hsts-hpkp-preloaded.test", &new_dynamic_pkp_state)); |
| |
| EXPECT_EQ(new_static_sts_state.upgrade_mode, static_sts_state.upgrade_mode); |
| EXPECT_EQ(new_static_sts_state.include_subdomains, |
| static_sts_state.include_subdomains); |
| EXPECT_EQ(new_static_pkp_state.include_subdomains, |
| static_pkp_state.include_subdomains); |
| EXPECT_EQ(new_static_pkp_state.spki_hashes, static_pkp_state.spki_hashes); |
| EXPECT_EQ(new_static_pkp_state.bad_spki_hashes, |
| static_pkp_state.bad_spki_hashes); |
| } |
| |
| // Make sure HSTS preserves a POST request's method and body. |
| TEST_F(HTTPSRequestTest, HSTSPreservesPosts) { |
| static const char kData[] = "hello world"; |
| |
| EmbeddedTestServer test_server(net::EmbeddedTestServer::TYPE_HTTPS); |
| RegisterDefaultHandlers(&test_server); |
| ASSERT_TRUE(test_server.Start()); |
| |
| // Per spec, TransportSecurityState expects a domain name, rather than an IP |
| // address, so a MockHostResolver is needed to redirect www.somewhere.com to |
| // the EmbeddedTestServer. |
| auto host_resolver = std::make_unique<MockHostResolver>(); |
| host_resolver->rules()->AddRule("www.somewhere.com", |
| test_server.GetIPLiteralString()); |
| |
| auto context_builder = CreateTestURLRequestContextBuilder(); |
| context_builder->set_host_resolver(std::move(host_resolver)); |
| auto& network_delegate = *context_builder->set_network_delegate( |
| std::make_unique<TestNetworkDelegate>()); |
| auto context = context_builder->Build(); |
| ASSERT_TRUE(context->transport_security_state()); |
| TransportSecurityState& transport_security_state = |
| *context->transport_security_state(); |
| // Force https for www.somewhere.com. |
| base::Time expiry = base::Time::Now() + base::Days(1000); |
| bool include_subdomains = false; |
| transport_security_state.AddHSTS("www.somewhere.com", expiry, |
| include_subdomains); |
| |
| TestDelegate d; |
| // Navigating to https://www.somewhere.com instead of https://127.0.0.1 will |
| // cause a certificate error. Ignore the error. |
| d.set_allow_certificate_errors(true); |
| |
| std::unique_ptr<URLRequest> req(context->CreateRequest( |
| GURL(base::StringPrintf("http://www.somewhere.com:%d/echo", |
| test_server.host_port_pair().port())), |
| DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->set_method("POST"); |
| req->set_upload(CreateSimpleUploadData(kData)); |
| |
| req->Start(); |
| d.RunUntilComplete(); |
| |
| EXPECT_EQ("https", req->url().scheme()); |
| EXPECT_EQ("POST", req->method()); |
| EXPECT_EQ(kData, d.data_received()); |
| |
| LoadTimingInfo load_timing_info; |
| network_delegate.GetLoadTimingInfoBeforeRedirect(&load_timing_info); |
| // LoadTimingInfo of HSTS redirects is similar to that of network cache hits |
| TestLoadTimingCacheHitNoNetwork(load_timing_info); |
| } |
| |
| // Make sure that the CORS headers are added to cross-origin HSTS redirects. |
| TEST_F(HTTPSRequestTest, HSTSCrossOriginAddHeaders) { |
| static const char kOriginHeaderValue[] = "http://www.example.com"; |
| |
| EmbeddedTestServer test_server(net::EmbeddedTestServer::TYPE_HTTPS); |
| test_server.ServeFilesFromSourceDirectory("net/data/ssl"); |
| ASSERT_TRUE(test_server.Start()); |
| |
| auto cert_verifier = std::make_unique<MockCertVerifier>(); |
| cert_verifier->set_default_result(OK); |
| |
| auto context_builder = CreateTestURLRequestContextBuilder(); |
| context_builder->SetCertVerifier(std::move(cert_verifier)); |
| auto context = context_builder->Build(); |
| ASSERT_TRUE(context->transport_security_state()); |
| TransportSecurityState& transport_security_state = |
| *context->transport_security_state(); |
| base::Time expiry = base::Time::Now() + base::Days(1); |
| bool include_subdomains = false; |
| transport_security_state.AddHSTS("example.net", expiry, include_subdomains); |
| |
| GURL hsts_http_url(base::StringPrintf("http://example.net:%d/somehstssite", |
| test_server.host_port_pair().port())); |
| GURL::Replacements replacements; |
| replacements.SetSchemeStr("https"); |
| GURL hsts_https_url = hsts_http_url.ReplaceComponents(replacements); |
| |
| TestDelegate d; |
| |
| std::unique_ptr<URLRequest> req(context->CreateRequest( |
| hsts_http_url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| // Set Origin header to simulate a cross-origin request. |
| HttpRequestHeaders request_headers; |
| request_headers.SetHeader("Origin", kOriginHeaderValue); |
| req->SetExtraRequestHeaders(request_headers); |
| |
| req->Start(); |
| d.RunUntilRedirect(); |
| |
| EXPECT_EQ(1, d.received_redirect_count()); |
| |
| const HttpResponseHeaders* headers = req->response_headers(); |
| std::string redirect_location; |
| EXPECT_TRUE( |
| headers->EnumerateHeader(nullptr, "Location", &redirect_location)); |
| EXPECT_EQ(hsts_https_url.spec(), redirect_location); |
| |
| std::string received_cors_header; |
| EXPECT_TRUE(headers->EnumerateHeader(nullptr, "Access-Control-Allow-Origin", |
| &received_cors_header)); |
| EXPECT_EQ(kOriginHeaderValue, received_cors_header); |
| |
| std::string received_corp_header; |
| EXPECT_TRUE(headers->EnumerateHeader(nullptr, "Cross-Origin-Resource-Policy", |
| &received_corp_header)); |
| EXPECT_EQ("Cross-Origin", received_corp_header); |
| } |
| |
| namespace { |
| |
| class SSLClientAuthTestDelegate : public TestDelegate { |
| public: |
| SSLClientAuthTestDelegate() { set_on_complete(base::DoNothing()); } |
| void OnCertificateRequested(URLRequest* request, |
| SSLCertRequestInfo* cert_request_info) override { |
| on_certificate_requested_count_++; |
| std::move(on_certificate_requested_).Run(); |
| } |
| void RunUntilCertificateRequested() { |
| base::RunLoop run_loop; |
| on_certificate_requested_ = run_loop.QuitClosure(); |
| run_loop.Run(); |
| } |
| int on_certificate_requested_count() { |
| return on_certificate_requested_count_; |
| } |
| |
| private: |
| int on_certificate_requested_count_ = 0; |
| base::OnceClosure on_certificate_requested_; |
| }; |
| |
| class TestSSLPrivateKey : public SSLPrivateKey { |
| public: |
| explicit TestSSLPrivateKey(scoped_refptr<SSLPrivateKey> key) |
| : key_(std::move(key)) {} |
| |
| void set_fail_signing(bool fail_signing) { fail_signing_ = fail_signing; } |
| int sign_count() const { return sign_count_; } |
| |
| std::string GetProviderName() override { return key_->GetProviderName(); } |
| std::vector<uint16_t> GetAlgorithmPreferences() override { |
| return key_->GetAlgorithmPreferences(); |
| } |
| void Sign(uint16_t algorithm, |
| base::span<const uint8_t> input, |
| SignCallback callback) override { |
| sign_count_++; |
| if (fail_signing_) { |
| base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask( |
| FROM_HERE, base::BindOnce(std::move(callback), |
| ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED, |
| std::vector<uint8_t>())); |
| } else { |
| key_->Sign(algorithm, input, std::move(callback)); |
| } |
| } |
| |
| private: |
| ~TestSSLPrivateKey() override = default; |
| |
| scoped_refptr<SSLPrivateKey> key_; |
| bool fail_signing_ = false; |
| int sign_count_ = 0; |
| }; |
| |
| } // namespace |
| |
| // TODO(davidben): Test the rest of the code. Specifically, |
| // - Filtering which certificates to select. |
| // - Getting a certificate request in an SSL renegotiation sending the |
| // HTTP request. |
| TEST_F(HTTPSRequestTest, ClientAuthNoCertificate) { |
| EmbeddedTestServer test_server(net::EmbeddedTestServer::TYPE_HTTPS); |
| net::SSLServerConfig ssl_config; |
| ssl_config.client_cert_type = |
| SSLServerConfig::ClientCertType::OPTIONAL_CLIENT_CERT; |
| test_server.SetSSLConfig(EmbeddedTestServer::CERT_OK, ssl_config); |
| RegisterDefaultHandlers(&test_server); |
| ASSERT_TRUE(test_server.Start()); |
| |
| SSLClientAuthTestDelegate d; |
| { |
| std::unique_ptr<URLRequest> r(default_context().CreateRequest( |
| test_server.GetURL("/defaultresponse"), DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| |
| r->Start(); |
| EXPECT_TRUE(r->is_pending()); |
| |
| d.RunUntilCertificateRequested(); |
| EXPECT_TRUE(r->is_pending()); |
| |
| EXPECT_EQ(1, d.on_certificate_requested_count()); |
| EXPECT_FALSE(d.received_data_before_response()); |
| EXPECT_EQ(0, d.bytes_received()); |
| |
| // Send no certificate. |
| // TODO(davidben): Get temporary client cert import (with keys) working on |
| // all platforms so we can test sending a cert as well. |
| r->ContinueWithCertificate(nullptr, nullptr); |
| |
| d.RunUntilComplete(); |
| |
| EXPECT_EQ(OK, d.request_status()); |
| EXPECT_EQ(1, d.response_started_count()); |
| EXPECT_FALSE(d.received_data_before_response()); |
| EXPECT_NE(0, d.bytes_received()); |
| } |
| } |
| |
| TEST_F(HTTPSRequestTest, ClientAuth) { |
| std::unique_ptr<FakeClientCertIdentity> identity = |
| FakeClientCertIdentity::CreateFromCertAndKeyFiles( |
| GetTestCertsDirectory(), "client_1.pem", "client_1.pk8"); |
| ASSERT_TRUE(identity); |
| scoped_refptr<TestSSLPrivateKey> private_key = |
| base::MakeRefCounted<TestSSLPrivateKey>(identity->ssl_private_key()); |
| |
| EmbeddedTestServer test_server(net::EmbeddedTestServer::TYPE_HTTPS); |
| net::SSLServerConfig ssl_config; |
| ssl_config.client_cert_type = |
| SSLServerConfig::ClientCertType::REQUIRE_CLIENT_CERT; |
| test_server.SetSSLConfig(EmbeddedTestServer::CERT_OK, ssl_config); |
| RegisterDefaultHandlers(&test_server); |
| ASSERT_TRUE(test_server.Start()); |
| |
| { |
| SSLClientAuthTestDelegate d; |
| std::unique_ptr<URLRequest> r(default_context().CreateRequest( |
| test_server.GetURL("/defaultresponse"), DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| |
| r->Start(); |
| EXPECT_TRUE(r->is_pending()); |
| |
| d.RunUntilCertificateRequested(); |
| EXPECT_TRUE(r->is_pending()); |
| |
| EXPECT_EQ(1, d.on_certificate_requested_count()); |
| EXPECT_FALSE(d.received_data_before_response()); |
| EXPECT_EQ(0, d.bytes_received()); |
| |
| // Send a certificate. |
| r->ContinueWithCertificate(identity->certificate(), private_key); |
| |
| d.RunUntilComplete(); |
| |
| EXPECT_EQ(OK, d.request_status()); |
| EXPECT_EQ(1, d.response_started_count()); |
| EXPECT_FALSE(d.received_data_before_response()); |
| EXPECT_NE(0, d.bytes_received()); |
| |
| // The private key should have been used. |
| EXPECT_EQ(1, private_key->sign_count()); |
| } |
| |
| // Close all connections and clear the session cache to force a new handshake. |
| default_context() |
| .http_transaction_factory() |
| ->GetSession() |
| ->CloseAllConnections(ERR_FAILED, "Very good reason"); |
| default_context() |
| .http_transaction_factory() |
| ->GetSession() |
| ->ClearSSLSessionCache(); |
| |
| // Connecting again should not call OnCertificateRequested. The identity is |
| // taken from the client auth cache. |
| { |
| SSLClientAuthTestDelegate d; |
| std::unique_ptr<URLRequest> r(default_context().CreateRequest( |
| test_server.GetURL("/defaultresponse"), DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| |
| r->Start(); |
| EXPECT_TRUE(r->is_pending()); |
| |
| d.RunUntilComplete(); |
| |
| EXPECT_EQ(OK, d.request_status()); |
| EXPECT_EQ(0, d.on_certificate_requested_count()); |
| EXPECT_FALSE(d.received_data_before_response()); |
| EXPECT_EQ(1, d.response_started_count()); |
| EXPECT_FALSE(d.received_data_before_response()); |
| EXPECT_NE(0, d.bytes_received()); |
| |
| // The private key should have been used. |
| EXPECT_EQ(2, private_key->sign_count()); |
| } |
| } |
| |
| // Test that private keys that fail to sign anything get evicted from the cache. |
| TEST_F(HTTPSRequestTest, ClientAuthFailSigning) { |
| std::unique_ptr<FakeClientCertIdentity> identity = |
| FakeClientCertIdentity::CreateFromCertAndKeyFiles( |
| GetTestCertsDirectory(), "client_1.pem", "client_1.pk8"); |
| ASSERT_TRUE(identity); |
| scoped_refptr<TestSSLPrivateKey> private_key = |
| base::MakeRefCounted<TestSSLPrivateKey>(identity->ssl_private_key()); |
| private_key->set_fail_signing(true); |
| |
| EmbeddedTestServer test_server(net::EmbeddedTestServer::TYPE_HTTPS); |
| net::SSLServerConfig ssl_config; |
| ssl_config.client_cert_type = |
| SSLServerConfig::ClientCertType::REQUIRE_CLIENT_CERT; |
| test_server.SetSSLConfig(EmbeddedTestServer::CERT_OK, ssl_config); |
| RegisterDefaultHandlers(&test_server); |
| ASSERT_TRUE(test_server.Start()); |
| |
| { |
| SSLClientAuthTestDelegate d; |
| std::unique_ptr<URLRequest> r(default_context().CreateRequest( |
| test_server.GetURL("/defaultresponse"), DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| |
| r->Start(); |
| EXPECT_TRUE(r->is_pending()); |
| |
| d.RunUntilCertificateRequested(); |
| EXPECT_TRUE(r->is_pending()); |
| |
| EXPECT_EQ(1, d.on_certificate_requested_count()); |
| EXPECT_FALSE(d.received_data_before_response()); |
| EXPECT_EQ(0, d.bytes_received()); |
| |
| // Send a certificate. |
| r->ContinueWithCertificate(identity->certificate(), private_key); |
| d.RunUntilComplete(); |
| |
| // The private key cannot sign anything, so we report an error. |
| EXPECT_EQ(ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED, d.request_status()); |
| EXPECT_EQ(1, d.response_started_count()); |
| EXPECT_FALSE(d.received_data_before_response()); |
| EXPECT_EQ(0, d.bytes_received()); |
| |
| // The private key should have been used. |
| EXPECT_EQ(1, private_key->sign_count()); |
| } |
| |
| // Close all connections and clear the session cache to force a new handshake. |
| default_context() |
| .http_transaction_factory() |
| ->GetSession() |
| ->CloseAllConnections(ERR_FAILED, "Very good reason"); |
| default_context() |
| .http_transaction_factory() |
| ->GetSession() |
| ->ClearSSLSessionCache(); |
| |
| // The bad identity should have been evicted from the cache, so connecting |
| // again should call OnCertificateRequested again. |
| { |
| SSLClientAuthTestDelegate d; |
| std::unique_ptr<URLRequest> r(default_context().CreateRequest( |
| test_server.GetURL("/defaultresponse"), DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| |
| r->Start(); |
| EXPECT_TRUE(r->is_pending()); |
| |
| d.RunUntilCertificateRequested(); |
| EXPECT_TRUE(r->is_pending()); |
| |
| EXPECT_EQ(1, d.on_certificate_requested_count()); |
| EXPECT_FALSE(d.received_data_before_response()); |
| EXPECT_EQ(0, d.bytes_received()); |
| |
| // There should have been no additional uses of the private key. |
| EXPECT_EQ(1, private_key->sign_count()); |
| } |
| } |
| |
| // Test that cached private keys that fail to sign anything trigger a |
| // retry. This is so we handle unplugged smartcards |
| // gracefully. https://crbug.com/813022. |
| TEST_F(HTTPSRequestTest, ClientAuthFailSigningRetry) { |
| std::unique_ptr<FakeClientCertIdentity> identity = |
| FakeClientCertIdentity::CreateFromCertAndKeyFiles( |
| GetTestCertsDirectory(), "client_1.pem", "client_1.pk8"); |
| ASSERT_TRUE(identity); |
| scoped_refptr<TestSSLPrivateKey> private_key = |
| base::MakeRefCounted<TestSSLPrivateKey>(identity->ssl_private_key()); |
| |
| EmbeddedTestServer test_server(net::EmbeddedTestServer::TYPE_HTTPS); |
| net::SSLServerConfig ssl_config; |
| ssl_config.client_cert_type = |
| SSLServerConfig::ClientCertType::REQUIRE_CLIENT_CERT; |
| test_server.SetSSLConfig(EmbeddedTestServer::CERT_OK, ssl_config); |
| RegisterDefaultHandlers(&test_server); |
| ASSERT_TRUE(test_server.Start()); |
| |
| // Connect with a client certificate to put it in the client auth cache. |
| { |
| SSLClientAuthTestDelegate d; |
| std::unique_ptr<URLRequest> r(default_context().CreateRequest( |
| test_server.GetURL("/defaultresponse"), DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| |
| r->Start(); |
| EXPECT_TRUE(r->is_pending()); |
| |
| d.RunUntilCertificateRequested(); |
| EXPECT_TRUE(r->is_pending()); |
| |
| EXPECT_EQ(1, d.on_certificate_requested_count()); |
| EXPECT_FALSE(d.received_data_before_response()); |
| EXPECT_EQ(0, d.bytes_received()); |
| |
| r->ContinueWithCertificate(identity->certificate(), private_key); |
| d.RunUntilComplete(); |
| |
| EXPECT_EQ(OK, d.request_status()); |
| EXPECT_EQ(1, d.response_started_count()); |
| EXPECT_FALSE(d.received_data_before_response()); |
| EXPECT_NE(0, d.bytes_received()); |
| |
| // The private key should have been used. |
| EXPECT_EQ(1, private_key->sign_count()); |
| } |
| |
| // Close all connections and clear the session cache to force a new handshake. |
| default_context() |
| .http_transaction_factory() |
| ->GetSession() |
| ->CloseAllConnections(ERR_FAILED, "Very good reason"); |
| default_context() |
| .http_transaction_factory() |
| ->GetSession() |
| ->ClearSSLSessionCache(); |
| |
| // Cause the private key to fail. Connecting again should attempt to use it, |
| // notice the failure, and then request a new identity via |
| // OnCertificateRequested. |
| private_key->set_fail_signing(true); |
| |
| { |
| SSLClientAuthTestDelegate d; |
| std::unique_ptr<URLRequest> r(default_context().CreateRequest( |
| test_server.GetURL("/defaultresponse"), DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| |
| r->Start(); |
| EXPECT_TRUE(r->is_pending()); |
| |
| d.RunUntilCertificateRequested(); |
| EXPECT_TRUE(r->is_pending()); |
| |
| // There was an additional signing call on the private key (the one which |
| // failed). |
| EXPECT_EQ(2, private_key->sign_count()); |
| |
| // That caused another OnCertificateRequested call. |
| EXPECT_EQ(1, d.on_certificate_requested_count()); |
| EXPECT_FALSE(d.received_data_before_response()); |
| EXPECT_EQ(0, d.bytes_received()); |
| } |
| } |
| |
| TEST_F(HTTPSRequestTest, ResumeTest) { |
| // Test that we attempt resume sessions when making two connections to the |
| // same host. |
| EmbeddedTestServer test_server(EmbeddedTestServer::TYPE_HTTPS); |
| RegisterDefaultHandlers(&test_server); |
| ASSERT_TRUE(test_server.Start()); |
| const auto url = test_server.GetURL("/"); |
| |
| default_context() |
| .http_transaction_factory() |
| ->GetSession() |
| ->ClearSSLSessionCache(); |
| |
| { |
| TestDelegate d; |
| std::unique_ptr<URLRequest> r(default_context().CreateRequest( |
| url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| |
| r->Start(); |
| EXPECT_TRUE(r->is_pending()); |
| |
| d.RunUntilComplete(); |
| |
| EXPECT_EQ(1, d.response_started_count()); |
| EXPECT_EQ(SSLInfo::HANDSHAKE_FULL, r->ssl_info().handshake_type); |
| } |
| |
| reinterpret_cast<HttpCache*>(default_context().http_transaction_factory()) |
| ->CloseAllConnections(ERR_FAILED, "Very good reason"); |
| |
| { |
| TestDelegate d; |
| std::unique_ptr<URLRequest> r(default_context().CreateRequest( |
| url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| |
| r->Start(); |
| EXPECT_TRUE(r->is_pending()); |
| |
| d.RunUntilComplete(); |
| |
| EXPECT_EQ(1, d.response_started_count()); |
| EXPECT_EQ(SSLInfo::HANDSHAKE_RESUME, r->ssl_info().handshake_type); |
| } |
| } |
| |
| // Test that sessions aren't resumed across URLRequestContexts. |
| TEST_F(HTTPSRequestTest, SSLSessionCacheShardTest) { |
| // Start a server. |
| EmbeddedTestServer test_server(EmbeddedTestServer::TYPE_HTTPS); |
| RegisterDefaultHandlers(&test_server); |
| ASSERT_TRUE(test_server.Start()); |
| const auto url = test_server.GetURL("/"); |
| |
| // Connect to the server once. This will add an entry to the session cache. |
| { |
| TestDelegate d; |
| std::unique_ptr<URLRequest> r(default_context().CreateRequest( |
| url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| |
| r->Start(); |
| EXPECT_TRUE(r->is_pending()); |
| |
| d.RunUntilComplete(); |
| |
| EXPECT_EQ(1, d.response_started_count()); |
| EXPECT_EQ(SSLInfo::HANDSHAKE_FULL, r->ssl_info().handshake_type); |
| } |
| |
| // Clear the socket pools and connect again. This should resume the previous |
| // session. |
| default_context() |
| .http_transaction_factory() |
| ->GetSession() |
| ->CloseAllConnections(ERR_FAILED, /*net_log_reason_utf8=*/""); |
| |
| { |
| TestDelegate d; |
| std::unique_ptr<URLRequest> r(default_context().CreateRequest( |
| url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| |
| r->Start(); |
| EXPECT_TRUE(r->is_pending()); |
| |
| d.RunUntilComplete(); |
| |
| EXPECT_EQ(1, d.response_started_count()); |
| EXPECT_EQ(SSLInfo::HANDSHAKE_RESUME, r->ssl_info().handshake_type); |
| } |
| |
| // Now fetch on a new URLRequestContext. This should not resume the session. |
| auto context_builder = CreateTestURLRequestContextBuilder(); |
| auto other_context = context_builder->Build(); |
| |
| { |
| TestDelegate d; |
| std::unique_ptr<URLRequest> r(other_context->CreateRequest( |
| url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| |
| r->Start(); |
| EXPECT_TRUE(r->is_pending()); |
| |
| d.RunUntilComplete(); |
| |
| EXPECT_EQ(1, d.response_started_count()); |
| EXPECT_EQ(SSLInfo::HANDSHAKE_FULL, r->ssl_info().handshake_type); |
| } |
| } |
| |
| // Test that sessions started with privacy mode enabled cannot be resumed when |
| // it is disabled, and vice versa. |
| TEST_F(HTTPSRequestTest, NoSessionResumptionBetweenPrivacyModes) { |
| // Start a server. |
| EmbeddedTestServer test_server(EmbeddedTestServer::TYPE_HTTPS); |
| RegisterDefaultHandlers(&test_server); |
| ASSERT_TRUE(test_server.Start()); |
| const auto url = test_server.GetURL("/"); |
| |
| auto ConnectAndCheckHandshake = [this, url](bool allow_credentials, |
| auto expected_handshake) { |
| // Construct request and indirectly set the privacy mode. |
| TestDelegate d; |
| std::unique_ptr<URLRequest> r(default_context().CreateRequest( |
| url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| r->set_allow_credentials(allow_credentials); |
| |
| // Start the request and check the SSL handshake type. |
| r->Start(); |
| EXPECT_TRUE(r->is_pending()); |
| d.RunUntilComplete(); |
| EXPECT_EQ(1, d.response_started_count()); |
| EXPECT_EQ(expected_handshake, r->ssl_info().handshake_type); |
| }; |
| |
| // Exhaustively check all pairs of privacy mode values. Note that we are using |
| // allow_credentials to indirectly enable/disable privacy mode. |
| const bool kAllowCredentialsValues[] = {false, true}; |
| for (const auto allow_creds_1 : kAllowCredentialsValues) { |
| for (const auto allow_creds_2 : kAllowCredentialsValues) { |
| SCOPED_TRACE(base::StringPrintf("allow_creds_1=%d, allow_creds_2=%d", |
| allow_creds_1, allow_creds_2)); |
| |
| // The session cache starts off empty, so we expect a full handshake. |
| ConnectAndCheckHandshake(allow_creds_1, SSLInfo::HANDSHAKE_FULL); |
| |
| // The second handshake depends on whether we are using the same session |
| // cache as the first request. |
| ConnectAndCheckHandshake(allow_creds_2, allow_creds_1 == allow_creds_2 |
| ? SSLInfo::HANDSHAKE_RESUME |
| : SSLInfo::HANDSHAKE_FULL); |
| // Flush both session caches. |
| auto* network_session = |
| default_context().http_transaction_factory()->GetSession(); |
| network_session->ClearSSLSessionCache(); |
| } |
| } |
| } |
| |
| class HTTPSFallbackTest : public TestWithTaskEnvironment { |
| public: |
| HTTPSFallbackTest() { |
| auto context_builder = CreateTestURLRequestContextBuilder(); |
| context_builder->set_ssl_config_service( |
| std::make_unique<TestSSLConfigService>(SSLContextConfig())); |
| context_ = context_builder->Build(); |
| } |
| ~HTTPSFallbackTest() override = default; |
| |
| protected: |
| void DoFallbackTest(const SSLServerConfig& ssl_config) { |
| DCHECK(!request_); |
| delegate_.set_allow_certificate_errors(true); |
| |
| EmbeddedTestServer test_server(EmbeddedTestServer::TYPE_HTTPS); |
| test_server.SetSSLConfig(EmbeddedTestServer::CERT_OK, ssl_config); |
| ASSERT_TRUE(test_server.Start()); |
| |
| request_ = |
| context_->CreateRequest(test_server.GetURL("/"), DEFAULT_PRIORITY, |
| &delegate_, TRAFFIC_ANNOTATION_FOR_TESTS); |
| request_->Start(); |
| |
| delegate_.RunUntilComplete(); |
| } |
| |
| void ExpectConnection(int version) { |
| EXPECT_EQ(1, delegate_.response_started_count()); |
| EXPECT_NE(0, delegate_.bytes_received()); |
| EXPECT_EQ(version, SSLConnectionStatusToVersion( |
| request_->ssl_info().connection_status)); |
| } |
| |
| void ExpectFailure(int error) { |
| EXPECT_EQ(1, delegate_.response_started_count()); |
| EXPECT_EQ(error, delegate_.request_status()); |
| } |
| |
| private: |
| TestDelegate delegate_; |
| std::unique_ptr<URLRequestContext> context_; |
| std::unique_ptr<URLRequest> request_; |
| }; |
| |
| // Tests the TLS 1.0 fallback doesn't happen. |
| TEST_F(HTTPSFallbackTest, TLSv1NoFallback) { |
| net::SSLServerConfig ssl_config; |
| ssl_config.client_hello_callback_for_testing = |
| base::BindRepeating([](const SSL_CLIENT_HELLO* client_hello) { |
| // Reject ClientHellos with version >= TLS 1.1. |
| return client_hello->version <= TLS1_VERSION; |
| }); |
| |
| ASSERT_NO_FATAL_FAILURE(DoFallbackTest(ssl_config)); |
| ExpectFailure(ERR_SSL_VERSION_OR_CIPHER_MISMATCH); |
| } |
| |
| // Tests the TLS 1.1 fallback doesn't happen. |
| TEST_F(HTTPSFallbackTest, TLSv1_1NoFallback) { |
| net::SSLServerConfig ssl_config; |
| ssl_config.client_hello_callback_for_testing = |
| base::BindRepeating([](const SSL_CLIENT_HELLO* client_hello) { |
| // Reject ClientHellos with version >= TLS 1.2. |
| return client_hello->version <= TLS1_1_VERSION; |
| }); |
| |
| ASSERT_NO_FATAL_FAILURE(DoFallbackTest(ssl_config)); |
| ExpectFailure(ERR_SSL_VERSION_OR_CIPHER_MISMATCH); |
| } |
| |
| // Tests the TLS 1.2 fallback doesn't happen. |
| TEST_F(HTTPSFallbackTest, TLSv1_2NoFallback) { |
| net::SSLServerConfig ssl_config; |
| ssl_config.client_hello_callback_for_testing = |
| base::BindRepeating([](const SSL_CLIENT_HELLO* client_hello) { |
| // Reject ClientHellos with a supported_versions extension. TLS 1.3 is |
| // signaled via an extension rather than the legacy version field. |
| const uint8_t* data; |
| size_t len; |
| return !SSL_early_callback_ctx_extension_get( |
| client_hello, TLSEXT_TYPE_supported_versions, &data, &len); |
| }); |
| |
| ASSERT_NO_FATAL_FAILURE(DoFallbackTest(ssl_config)); |
| ExpectFailure(ERR_SSL_VERSION_OR_CIPHER_MISMATCH); |
| } |
| |
| class HTTPSSessionTest : public TestWithTaskEnvironment { |
| public: |
| HTTPSSessionTest() { |
| auto context_builder = CreateTestURLRequestContextBuilder(); |
| context_builder->SetCertVerifier(std::make_unique<MockCertVerifier>()); |
| default_context_ = context_builder->Build(); |
| |
| default_cert_verifier().set_default_result(OK); |
| } |
| ~HTTPSSessionTest() override = default; |
| |
| URLRequestContext& default_context() { return *default_context_; } |
| MockCertVerifier& default_cert_verifier() { |
| // This cast is safe because we provided a MockCertVerifier in the ctor. |
| return *static_cast<MockCertVerifier*>(default_context_->cert_verifier()); |
| } |
| |
| private: |
| std::unique_ptr<URLRequestContext> default_context_; |
| }; |
| |
| // Tests that session resumption is not attempted if an invalid certificate |
| // is presented. |
| TEST_F(HTTPSSessionTest, DontResumeSessionsForInvalidCertificates) { |
| EmbeddedTestServer test_server(EmbeddedTestServer::TYPE_HTTPS); |
| RegisterDefaultHandlers(&test_server); |
| ASSERT_TRUE(test_server.Start()); |
| const auto url = test_server.GetURL("/"); |
| |
| default_context() |
| .http_transaction_factory() |
| ->GetSession() |
| ->ClearSSLSessionCache(); |
| |
| // Simulate the certificate being expired and attempt a connection. |
| default_cert_verifier().set_default_result(ERR_CERT_DATE_INVALID); |
| { |
| TestDelegate d; |
| std::unique_ptr<URLRequest> r(default_context().CreateRequest( |
| url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| |
| r->Start(); |
| EXPECT_TRUE(r->is_pending()); |
| |
| d.RunUntilComplete(); |
| |
| EXPECT_EQ(1, d.response_started_count()); |
| } |
| |
| reinterpret_cast<HttpCache*>(default_context().http_transaction_factory()) |
| ->CloseAllConnections(ERR_FAILED, "Very good reason"); |
| |
| // Now change the certificate to be acceptable (so that the response is |
| // loaded), and ensure that no session id is presented to the peer. |
| default_cert_verifier().set_default_result(OK); |
| { |
| TestDelegate d; |
| std::unique_ptr<URLRequest> r(default_context().CreateRequest( |
| url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| |
| r->Start(); |
| EXPECT_TRUE(r->is_pending()); |
| |
| d.RunUntilComplete(); |
| |
| EXPECT_EQ(1, d.response_started_count()); |
| EXPECT_EQ(SSLInfo::HANDSHAKE_FULL, r->ssl_info().handshake_type); |
| } |
| } |
| |
| // Interceptor to check that secure DNS has been disabled. Secure DNS should be |
| // disabled for any network fetch triggered during certificate verification as |
| // it could cause a deadlock. |
| class SecureDnsInterceptor : public net::URLRequestInterceptor { |
| public: |
| SecureDnsInterceptor() = default; |
| ~SecureDnsInterceptor() override = default; |
| |
| private: |
| // URLRequestInterceptor implementation: |
| std::unique_ptr<net::URLRequestJob> MaybeInterceptRequest( |
| net::URLRequest* request) const override { |
| EXPECT_EQ(SecureDnsPolicy::kDisable, request->secure_dns_policy()); |
| return nullptr; |
| } |
| }; |
| |
| class HTTPSCertNetFetchingTest : public HTTPSRequestTest { |
| public: |
| HTTPSCertNetFetchingTest() = default; |
| |
| void SetUp() override { |
| cert_net_fetcher_ = base::MakeRefCounted<CertNetFetcherURLRequest>(); |
| auto cert_verifier = |
| CertVerifier::CreateDefaultWithoutCaching(cert_net_fetcher_); |
| updatable_cert_verifier_ = cert_verifier.get(); |
| |
| auto context_builder = CreateTestURLRequestContextBuilder(); |
| context_builder->SetCertVerifier(std::make_unique<CachingCertVerifier>( |
| std::make_unique<CoalescingCertVerifier>(std::move(cert_verifier)))); |
| context_ = context_builder->Build(); |
| |
| net::URLRequestFilter::GetInstance()->AddHostnameInterceptor( |
| "http", "127.0.0.1", std::make_unique<SecureDnsInterceptor>()); |
| |
| cert_net_fetcher_->SetURLRequestContext(context_.get()); |
| context_->cert_verifier()->SetConfig(GetCertVerifierConfig()); |
| } |
| |
| void TearDown() override { |
| cert_net_fetcher_->Shutdown(); |
| net::URLRequestFilter::GetInstance()->ClearHandlers(); |
| } |
| |
| void DoConnectionWithDelegate( |
| base::StringPiece hostname, |
| const EmbeddedTestServer::ServerCertificateConfig& cert_config, |
| TestDelegate* delegate, |
| SSLInfo* out_ssl_info) { |
| // Always overwrite |out_ssl_info|. |
| out_ssl_info->Reset(); |
| |
| EmbeddedTestServer test_server(EmbeddedTestServer::TYPE_HTTPS); |
| test_server.SetSSLConfig(cert_config); |
| RegisterDefaultHandlers(&test_server); |
| ASSERT_TRUE(test_server.Start()); |
| |
| delegate->set_allow_certificate_errors(true); |
| std::unique_ptr<URLRequest> r(context_->CreateRequest( |
| test_server.GetURL(hostname, "/"), DEFAULT_PRIORITY, delegate, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| r->Start(); |
| |
| delegate->RunUntilComplete(); |
| EXPECT_EQ(1, delegate->response_started_count()); |
| |
| *out_ssl_info = r->ssl_info(); |
| } |
| |
| void DoConnectionWithDelegate( |
| const EmbeddedTestServer::ServerCertificateConfig& cert_config, |
| TestDelegate* delegate, |
| SSLInfo* out_ssl_info) { |
| DoConnectionWithDelegate("127.0.0.1", cert_config, delegate, out_ssl_info); |
| } |
| |
| void DoConnection( |
| base::StringPiece hostname, |
| const EmbeddedTestServer::ServerCertificateConfig& cert_config, |
| CertStatus* out_cert_status) { |
| // Always overwrite |out_cert_status|. |
| *out_cert_status = 0; |
| |
| TestDelegate d; |
| SSLInfo ssl_info; |
| ASSERT_NO_FATAL_FAILURE( |
| DoConnectionWithDelegate(hostname, cert_config, &d, &ssl_info)); |
| |
| *out_cert_status = ssl_info.cert_status; |
| } |
| |
| void DoConnection( |
| const EmbeddedTestServer::ServerCertificateConfig& cert_config, |
| CertStatus* out_cert_status) { |
| DoConnection("127.0.0.1", cert_config, out_cert_status); |
| } |
| |
| protected: |
| // GetCertVerifierConfig() configures the URLRequestContext that will be used |
| // for making connections to the testserver. This can be overridden in test |
| // subclasses for different behaviour. |
| virtual CertVerifier::Config GetCertVerifierConfig() { |
| CertVerifier::Config config; |
| return config; |
| } |
| |
| void UpdateCertVerifier(scoped_refptr<CRLSet> crl_set) { |
| net::CertVerifyProcFactory::ImplParams params; |
| params.crl_set = std::move(crl_set); |
| updatable_cert_verifier_->UpdateVerifyProcData(cert_net_fetcher_, params); |
| } |
| |
| scoped_refptr<CertNetFetcherURLRequest> cert_net_fetcher_; |
| std::unique_ptr<URLRequestContext> context_; |
| raw_ptr<CertVerifierWithUpdatableProc> updatable_cert_verifier_; |
| }; |
| |
| // The test EV policy OID used for generated certs. |
| static const char kEVTestCertPolicy[] = "1.3.6.1.4.1.11129.2.4.1"; |
| |
| class HTTPSEVTest : public HTTPSCertNetFetchingTest { |
| public: |
| void SetUp() override { |
| HTTPSCertNetFetchingTest::SetUp(); |
| |
| scoped_refptr<X509Certificate> root_cert = |
| ImportCertFromFile(GetTestCertsDirectory(), "root_ca_cert.pem"); |
| ASSERT_TRUE(root_cert); |
| |
| ev_test_policy_ = std::make_unique<ScopedTestEVPolicy>( |
| EVRootCAMetadata::GetInstance(), |
| X509Certificate::CalculateFingerprint256(root_cert->cert_buffer()), |
| kEVTestCertPolicy); |
| } |
| |
| void TearDown() override { HTTPSCertNetFetchingTest::TearDown(); } |
| |
| private: |
| std::unique_ptr<ScopedTestEVPolicy> ev_test_policy_; |
| }; |
| |
| class HTTPSOCSPTest : public HTTPSCertNetFetchingTest { |
| public: |
| CertVerifier::Config GetCertVerifierConfig() override { |
| CertVerifier::Config config; |
| config.enable_rev_checking = true; |
| return config; |
| } |
| }; |
| |
| static bool UsingBuiltinCertVerifier() { |
| #if BUILDFLAG(IS_FUCHSIA) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || \ |
| BUILDFLAG(CHROME_ROOT_STORE_ONLY) |
| return true; |
| #elif BUILDFLAG(CHROME_ROOT_STORE_OPTIONAL) |
| return base::FeatureList::IsEnabled(features::kChromeRootStoreUsed); |
| #else |
| return false; |
| #endif |
| } |
| |
| // SystemSupportsHardFailRevocationChecking returns true iff the current |
| // operating system supports revocation checking and can distinguish between |
| // situations where a given certificate lacks any revocation information (eg: |
| // no CRLDistributionPoints and no OCSP Responder AuthorityInfoAccess) and when |
| // revocation information cannot be obtained (eg: the CRL was unreachable). |
| // If it does not, then tests which rely on 'hard fail' behaviour should be |
| // skipped. |
| static bool SystemSupportsHardFailRevocationChecking() { |
| return UsingBuiltinCertVerifier(); |
| } |
| |
| // SystemUsesChromiumEVMetadata returns true iff the current operating system |
| // uses Chromium's EV metadata (i.e. EVRootCAMetadata). If it does not, then |
| // several tests are effected because our testing EV certificate won't be |
| // recognised as EV. |
| static bool SystemUsesChromiumEVMetadata() { |
| if (UsingBuiltinCertVerifier()) |
| return true; |
| #if defined(PLATFORM_USES_CHROMIUM_EV_METADATA) |
| return true; |
| #else |
| return false; |
| #endif |
| } |
| |
| static bool SystemSupportsOCSP() { |
| #if BUILDFLAG(IS_ANDROID) |
| // Unsupported, see http://crbug.com/117478. |
| return false; |
| #else |
| return true; |
| #endif |
| } |
| |
| static bool SystemSupportsOCSPStapling() { |
| if (UsingBuiltinCertVerifier()) |
| return true; |
| #if BUILDFLAG(IS_ANDROID) |
| return false; |
| #else |
| return true; |
| #endif |
| } |
| |
| static bool SystemSupportsCRLSets() { |
| if (UsingBuiltinCertVerifier()) |
| return true; |
| #if BUILDFLAG(IS_ANDROID) |
| return false; |
| #else |
| return true; |
| #endif |
| } |
| |
| TEST_F(HTTPSEVTest, EVCheckNoOCSP) { |
| if (!SystemUsesChromiumEVMetadata()) { |
| LOG(WARNING) << "Skipping test because system doesn't support EV"; |
| return; |
| } |
| EmbeddedTestServer::ServerCertificateConfig cert_config; |
| cert_config.policy_oids = {kEVTestCertPolicy}; |
| |
| CertStatus cert_status; |
| DoConnection(cert_config, &cert_status); |
| |
| EXPECT_EQ(0u, cert_status & CERT_STATUS_ALL_ERRORS); |
| |
| EXPECT_EQ(SystemUsesChromiumEVMetadata(), |
| static_cast<bool>(cert_status & CERT_STATUS_IS_EV)); |
| |
| EXPECT_FALSE(cert_status & CERT_STATUS_REV_CHECKING_ENABLED); |
| } |
| |
| TEST_F(HTTPSOCSPTest, Valid) { |
| if (!SystemSupportsOCSP()) { |
| LOG(WARNING) << "Skipping test because system doesn't support OCSP"; |
| return; |
| } |
| |
| EmbeddedTestServer::ServerCertificateConfig cert_config; |
| cert_config.ocsp_config = EmbeddedTestServer::OCSPConfig( |
| {{OCSPRevocationStatus::GOOD, |
| EmbeddedTestServer::OCSPConfig::SingleResponse::Date::kValid}}); |
| |
| CertStatus cert_status; |
| DoConnection(cert_config, &cert_status); |
| |
| EXPECT_EQ(0u, cert_status & CERT_STATUS_ALL_ERRORS); |
| |
| EXPECT_TRUE(cert_status & CERT_STATUS_REV_CHECKING_ENABLED); |
| } |
| |
| TEST_F(HTTPSOCSPTest, Revoked) { |
| if (!SystemSupportsOCSP()) { |
| LOG(WARNING) << "Skipping test because system doesn't support OCSP"; |
| return; |
| } |
| |
| EmbeddedTestServer::ServerCertificateConfig cert_config; |
| cert_config.ocsp_config = EmbeddedTestServer::OCSPConfig( |
| {{OCSPRevocationStatus::REVOKED, |
| EmbeddedTestServer::OCSPConfig::SingleResponse::Date::kValid}}); |
| |
| CertStatus cert_status; |
| DoConnection(cert_config, &cert_status); |
| |
| EXPECT_EQ(CERT_STATUS_REVOKED, cert_status & CERT_STATUS_ALL_ERRORS); |
| EXPECT_FALSE(cert_status & CERT_STATUS_IS_EV); |
| EXPECT_TRUE(cert_status & CERT_STATUS_REV_CHECKING_ENABLED); |
| } |
| |
| TEST_F(HTTPSOCSPTest, Invalid) { |
| if (!SystemSupportsOCSP()) { |
| LOG(WARNING) << "Skipping test because system doesn't support OCSP"; |
| return; |
| } |
| |
| EmbeddedTestServer::ServerCertificateConfig cert_config; |
| cert_config.ocsp_config = EmbeddedTestServer::OCSPConfig( |
| EmbeddedTestServer::OCSPConfig::ResponseType::kInvalidResponse); |
| |
| CertStatus cert_status; |
| DoConnection(cert_config, &cert_status); |
| |
| // Without a positive OCSP response, we shouldn't show the EV status, but also |
| // should not show any revocation checking errors. |
| EXPECT_FALSE(cert_status & CERT_STATUS_IS_EV); |
| EXPECT_EQ(0u, cert_status & CERT_STATUS_ALL_ERRORS); |
| EXPECT_TRUE(cert_status & CERT_STATUS_REV_CHECKING_ENABLED); |
| } |
| |
| TEST_F(HTTPSOCSPTest, IntermediateValid) { |
| if (!SystemSupportsOCSP()) { |
| LOG(WARNING) << "Skipping test because system doesn't support OCSP"; |
| return; |
| } |
| |
| EmbeddedTestServer::ServerCertificateConfig cert_config; |
| cert_config.intermediate = EmbeddedTestServer::IntermediateType::kInHandshake; |
| cert_config.ocsp_config = EmbeddedTestServer::OCSPConfig( |
| {{OCSPRevocationStatus::GOOD, |
| EmbeddedTestServer::OCSPConfig::SingleResponse::Date::kValid}}); |
| cert_config.intermediate_ocsp_config = EmbeddedTestServer::OCSPConfig( |
| {{OCSPRevocationStatus::GOOD, |
| EmbeddedTestServer::OCSPConfig::SingleResponse::Date::kValid}}); |
| |
| CertStatus cert_status; |
| DoConnection(cert_config, &cert_status); |
| |
| EXPECT_EQ(0u, cert_status & CERT_STATUS_ALL_ERRORS); |
| |
| EXPECT_TRUE(cert_status & CERT_STATUS_REV_CHECKING_ENABLED); |
| } |
| |
| TEST_F(HTTPSOCSPTest, IntermediateResponseOldButStillValid) { |
| if (!SystemSupportsOCSP()) { |
| LOG(WARNING) << "Skipping test because system doesn't support OCSP"; |
| return; |
| } |
| |
| EmbeddedTestServer::ServerCertificateConfig cert_config; |
| cert_config.intermediate = EmbeddedTestServer::IntermediateType::kInHandshake; |
| cert_config.ocsp_config = EmbeddedTestServer::OCSPConfig( |
| {{OCSPRevocationStatus::GOOD, |
| EmbeddedTestServer::OCSPConfig::SingleResponse::Date::kValid}}); |
| // Use an OCSP response for the intermediate that would be too old for a leaf |
| // cert, but is still valid for an intermediate. |
| cert_config.intermediate_ocsp_config = EmbeddedTestServer::OCSPConfig( |
| {{OCSPRevocationStatus::REVOKED, |
| EmbeddedTestServer::OCSPConfig::SingleResponse::Date::kLong}}); |
| |
| CertStatus cert_status; |
| DoConnection(cert_config, &cert_status); |
| |
| EXPECT_EQ(CERT_STATUS_REVOKED, cert_status & CERT_STATUS_ALL_ERRORS); |
| EXPECT_TRUE(cert_status & CERT_STATUS_REV_CHECKING_ENABLED); |
| } |
| |
| TEST_F(HTTPSOCSPTest, IntermediateResponseTooOldKnownRoot) { |
| if (!SystemSupportsOCSP()) { |
| LOG(WARNING) << "Skipping test because system doesn't support OCSP"; |
| return; |
| } |
| |
| scoped_refptr<X509Certificate> root_cert = |
| ImportCertFromFile(GetTestCertsDirectory(), "root_ca_cert.pem"); |
| ASSERT_TRUE(root_cert); |
| ScopedTestKnownRoot scoped_known_root(root_cert.get()); |
| |
| EmbeddedTestServer::ServerCertificateConfig cert_config; |
| cert_config.intermediate = EmbeddedTestServer::IntermediateType::kInHandshake; |
| cert_config.ocsp_config = EmbeddedTestServer::OCSPConfig( |
| {{OCSPRevocationStatus::GOOD, |
| EmbeddedTestServer::OCSPConfig::SingleResponse::Date::kValid}}); |
| cert_config.intermediate_ocsp_config = EmbeddedTestServer::OCSPConfig( |
| {{OCSPRevocationStatus::REVOKED, |
| EmbeddedTestServer::OCSPConfig::SingleResponse::Date::kLonger}}); |
| cert_config.dns_names = {"example.com"}; |
| |
| CertStatus cert_status; |
| DoConnection("example.com", cert_config, &cert_status); |
| |
| if (UsingBuiltinCertVerifier()) { |
| // The intermediate certificate is marked as a known root and has an OCSP |
| // response indicating REVOKED status, but the response is too old |
| // according to the Baseline Requirements, thus the response should be |
| // ignored and the verification succeeds. |
| EXPECT_EQ(0u, cert_status & CERT_STATUS_ALL_ERRORS); |
| } else { |
| EXPECT_EQ(CERT_STATUS_REVOKED, cert_status & CERT_STATUS_ALL_ERRORS); |
| } |
| EXPECT_TRUE(cert_status & CERT_STATUS_REV_CHECKING_ENABLED); |
| } |
| |
| TEST_F(HTTPSOCSPTest, IntermediateResponseTooOld) { |
| if (!SystemSupportsOCSP()) { |
| LOG(WARNING) << "Skipping test because system doesn't support OCSP"; |
| return; |
| } |
| |
| EmbeddedTestServer::ServerCertificateConfig cert_config; |
| cert_config.intermediate = EmbeddedTestServer::IntermediateType::kInHandshake; |
| cert_config.ocsp_config = EmbeddedTestServer::OCSPConfig( |
| {{OCSPRevocationStatus::GOOD, |
| EmbeddedTestServer::OCSPConfig::SingleResponse::Date::kValid}}); |
| cert_config.intermediate_ocsp_config = EmbeddedTestServer::OCSPConfig( |
| {{OCSPRevocationStatus::REVOKED, |
| EmbeddedTestServer::OCSPConfig::SingleResponse::Date::kLonger}}); |
| |
| CertStatus cert_status; |
| DoConnection(cert_config, &cert_status); |
| |
| // The test root is not a known root, therefore the Baseline Requirements |
| // limits on maximum age of a response do not apply and the intermediate OCSP |
| // response indicating revoked is honored. |
| EXPECT_EQ(CERT_STATUS_REVOKED, cert_status & CERT_STATUS_ALL_ERRORS); |
| EXPECT_TRUE(cert_status & CERT_STATUS_REV_CHECKING_ENABLED); |
| } |
| |
| TEST_F(HTTPSOCSPTest, IntermediateRevoked) { |
| if (!SystemSupportsOCSP()) { |
| LOG(WARNING) << "Skipping test because system doesn't support OCSP"; |
| return; |
| } |
| |
| EmbeddedTestServer::ServerCertificateConfig cert_config; |
| cert_config.intermediate = EmbeddedTestServer::IntermediateType::kInHandshake; |
| cert_config.ocsp_config = EmbeddedTestServer::OCSPConfig( |
| {{OCSPRevocationStatus::GOOD, |
| EmbeddedTestServer::OCSPConfig::SingleResponse::Date::kValid}}); |
| cert_config.intermediate_ocsp_config = EmbeddedTestServer::OCSPConfig( |
| {{OCSPRevocationStatus::REVOKED, |
| EmbeddedTestServer::OCSPConfig::SingleResponse::Date::kValid}}); |
| |
| CertStatus cert_status; |
| DoConnection(cert_config, &cert_status); |
| |
| EXPECT_EQ(CERT_STATUS_REVOKED, cert_status & CERT_STATUS_ALL_ERRORS); |
| EXPECT_TRUE(cert_status & CERT_STATUS_REV_CHECKING_ENABLED); |
| } |
| |
| TEST_F(HTTPSOCSPTest, ValidStapled) { |
| if (!SystemSupportsOCSPStapling()) { |
| LOG(WARNING) |
| << "Skipping test because system doesn't support OCSP stapling"; |
| return; |
| } |
| |
| EmbeddedTestServer::ServerCertificateConfig cert_config; |
| |
| // AIA OCSP url is included, but does not return a successful ocsp response. |
| cert_config.ocsp_config = EmbeddedTestServer::OCSPConfig( |
| EmbeddedTestServer::OCSPConfig::ResponseType::kTryLater); |
| |
| cert_config.stapled_ocsp_config = EmbeddedTestServer::OCSPConfig( |
| {{OCSPRevocationStatus::GOOD, |
| EmbeddedTestServer::OCSPConfig::SingleResponse::Date::kValid}}); |
| |
| CertStatus cert_status; |
| DoConnection(cert_config, &cert_status); |
| |
| EXPECT_EQ(0u, cert_status & CERT_STATUS_ALL_ERRORS); |
| EXPECT_TRUE(cert_status & CERT_STATUS_REV_CHECKING_ENABLED); |
| } |
| |
| TEST_F(HTTPSOCSPTest, RevokedStapled) { |
| if (!SystemSupportsOCSPStapling()) { |
| LOG(WARNING) |
| << "Skipping test because system doesn't support OCSP stapling"; |
| return; |
| } |
| |
| EmbeddedTestServer::ServerCertificateConfig cert_config; |
| |
| // AIA OCSP url is included, but does not return a successful ocsp response. |
| cert_config.ocsp_config = EmbeddedTestServer::OCSPConfig( |
| EmbeddedTestServer::OCSPConfig::ResponseType::kTryLater); |
| |
| cert_config.stapled_ocsp_config = EmbeddedTestServer::OCSPConfig( |
| {{OCSPRevocationStatus::REVOKED, |
| EmbeddedTestServer::OCSPConfig::SingleResponse::Date::kValid}}); |
| |
| CertStatus cert_status; |
| DoConnection(cert_config, &cert_status); |
| |
| EXPECT_EQ(CERT_STATUS_REVOKED, cert_status & CERT_STATUS_ALL_ERRORS); |
| EXPECT_TRUE(cert_status & CERT_STATUS_REV_CHECKING_ENABLED); |
| } |
| |
| TEST_F(HTTPSOCSPTest, OldStapledAndInvalidAIA) { |
| if (!SystemSupportsOCSPStapling()) { |
| LOG(WARNING) |
| << "Skipping test because system doesn't support OCSP stapling"; |
| return; |
| } |
| |
| EmbeddedTestServer::ServerCertificateConfig cert_config; |
| |
| // Stapled response indicates good, but is too old. |
| cert_config.stapled_ocsp_config = EmbeddedTestServer::OCSPConfig( |
| {{OCSPRevocationStatus::GOOD, |
| EmbeddedTestServer::OCSPConfig::SingleResponse::Date::kOld}}); |
| |
| // AIA OCSP url is included, but does not return a successful ocsp response. |
| cert_config.ocsp_config = EmbeddedTestServer::OCSPConfig( |
| EmbeddedTestServer::OCSPConfig::ResponseType::kTryLater); |
| |
| CertStatus cert_status; |
| DoConnection(cert_config, &cert_status); |
| |
| EXPECT_EQ(0u, cert_status & CERT_STATUS_ALL_ERRORS); |
| EXPECT_TRUE(cert_status & CERT_STATUS_REV_CHECKING_ENABLED); |
| } |
| |
| TEST_F(HTTPSOCSPTest, OldStapledButValidAIA) { |
| if (!SystemSupportsOCSPStapling()) { |
| LOG(WARNING) |
| << "Skipping test because system doesn't support OCSP stapling"; |
| return; |
| } |
| |
| EmbeddedTestServer::ServerCertificateConfig cert_config; |
| |
| // Stapled response indicates good, but response is too old. |
| cert_config.stapled_ocsp_config = EmbeddedTestServer::OCSPConfig( |
| {{OCSPRevocationStatus::GOOD, |
| EmbeddedTestServer::OCSPConfig::SingleResponse::Date::kOld}}); |
| |
| // AIA OCSP url is included, and returns a successful ocsp response. |
| cert_config.ocsp_config = EmbeddedTestServer::OCSPConfig( |
| {{OCSPRevocationStatus::GOOD, |
| EmbeddedTestServer::OCSPConfig::SingleResponse::Date::kValid}}); |
| |
| CertStatus cert_status; |
| DoConnection(cert_config, &cert_status); |
| |
| EXPECT_EQ(0u, cert_status & CERT_STATUS_ALL_ERRORS); |
| EXPECT_TRUE(cert_status & CERT_STATUS_REV_CHECKING_ENABLED); |
| } |
| |
| static const struct OCSPVerifyTestData { |
| EmbeddedTestServer::OCSPConfig ocsp_config; |
| OCSPVerifyResult::ResponseStatus expected_response_status; |
| // |expected_cert_status| is only used if |expected_response_status| is |
| // PROVIDED. |
| OCSPRevocationStatus expected_cert_status; |
| } kOCSPVerifyData[] = { |
| // 0 |
| {EmbeddedTestServer::OCSPConfig( |
| {{OCSPRevocationStatus::GOOD, |
| EmbeddedTestServer::OCSPConfig::SingleResponse::Date::kValid}}, |
| EmbeddedTestServer::OCSPConfig::Produced::kValid), |
| OCSPVerifyResult::PROVIDED, OCSPRevocationStatus::GOOD}, |
| |
| // 1 |
| {EmbeddedTestServer::OCSPConfig( |
| {{OCSPRevocationStatus::GOOD, |
| EmbeddedTestServer::OCSPConfig::SingleResponse::Date::kOld}}, |
| EmbeddedTestServer::OCSPConfig::Produced::kValid), |
| OCSPVerifyResult::INVALID_DATE, OCSPRevocationStatus::UNKNOWN}, |
| |
| // 2 |
| {EmbeddedTestServer::OCSPConfig( |
| {{OCSPRevocationStatus::GOOD, |
| EmbeddedTestServer::OCSPConfig::SingleResponse::Date::kEarly}}, |
| EmbeddedTestServer::OCSPConfig::Produced::kValid), |
| OCSPVerifyResult::INVALID_DATE, OCSPRevocationStatus::UNKNOWN}, |
| |
| // 3 |
| {EmbeddedTestServer::OCSPConfig( |
| {{OCSPRevocationStatus::GOOD, |
| EmbeddedTestServer::OCSPConfig::SingleResponse::Date::kLong}}, |
| EmbeddedTestServer::OCSPConfig::Produced::kValid), |
| OCSPVerifyResult::INVALID_DATE, OCSPRevocationStatus::UNKNOWN}, |
| |
| // 4 |
| {EmbeddedTestServer::OCSPConfig( |
| EmbeddedTestServer::OCSPConfig::ResponseType::kTryLater), |
| OCSPVerifyResult::ERROR_RESPONSE, OCSPRevocationStatus::UNKNOWN}, |
| |
| // 5 |
| {EmbeddedTestServer::OCSPConfig( |
| EmbeddedTestServer::OCSPConfig::ResponseType::kInvalidResponse), |
| OCSPVerifyResult::PARSE_RESPONSE_ERROR, OCSPRevocationStatus::UNKNOWN}, |
| |
| // 6 |
| {EmbeddedTestServer::OCSPConfig( |
| EmbeddedTestServer::OCSPConfig::ResponseType::kInvalidResponseData), |
| OCSPVerifyResult::PARSE_RESPONSE_DATA_ERROR, |
| OCSPRevocationStatus::UNKNOWN}, |
| |
| // 7 |
| {EmbeddedTestServer::OCSPConfig( |
| {{OCSPRevocationStatus::REVOKED, |
| EmbeddedTestServer::OCSPConfig::SingleResponse::Date::kEarly}}, |
| EmbeddedTestServer::OCSPConfig::Produced::kValid), |
| OCSPVerifyResult::INVALID_DATE, OCSPRevocationStatus::UNKNOWN}, |
| |
| // 8 |
| {EmbeddedTestServer::OCSPConfig( |
| {{OCSPRevocationStatus::UNKNOWN, |
| EmbeddedTestServer::OCSPConfig::SingleResponse::Date::kValid}}, |
| EmbeddedTestServer::OCSPConfig::Produced::kValid), |
| OCSPVerifyResult::PROVIDED, OCSPRevocationStatus::UNKNOWN}, |
| |
| // 9 |
| {EmbeddedTestServer::OCSPConfig( |
| {{OCSPRevocationStatus::UNKNOWN, |
| EmbeddedTestServer::OCSPConfig::SingleResponse::Date::kOld}}, |
| EmbeddedTestServer::OCSPConfig::Produced::kValid), |
| OCSPVerifyResult::INVALID_DATE, OCSPRevocationStatus::UNKNOWN}, |
| |
| // 10 |
| {EmbeddedTestServer::OCSPConfig( |
| {{OCSPRevocationStatus::UNKNOWN, |
| EmbeddedTestServer::OCSPConfig::SingleResponse::Date::kEarly}}, |
| EmbeddedTestServer::OCSPConfig::Produced::kValid), |
| OCSPVerifyResult::INVALID_DATE, OCSPRevocationStatus::UNKNOWN}, |
| |
| // 11 |
| {EmbeddedTestServer::OCSPConfig( |
| {{OCSPRevocationStatus::GOOD, |
| EmbeddedTestServer::OCSPConfig::SingleResponse::Date::kValid}}, |
| EmbeddedTestServer::OCSPConfig::Produced::kBeforeCert), |
| OCSPVerifyResult::BAD_PRODUCED_AT, OCSPRevocationStatus::UNKNOWN}, |
| |
| // 12 |
| {EmbeddedTestServer::OCSPConfig( |
| {{OCSPRevocationStatus::GOOD, |
| EmbeddedTestServer::OCSPConfig::SingleResponse::Date::kValid}}, |
| EmbeddedTestServer::OCSPConfig::Produced::kAfterCert), |
| OCSPVerifyResult::BAD_PRODUCED_AT, OCSPRevocationStatus::UNKNOWN}, |
| |
| // 13 |
| {EmbeddedTestServer::OCSPConfig( |
| {{OCSPRevocationStatus::GOOD, |
| EmbeddedTestServer::OCSPConfig::SingleResponse::Date::kOld}, |
| {OCSPRevocationStatus::GOOD, |
| EmbeddedTestServer::OCSPConfig::SingleResponse::Date::kValid}}, |
| EmbeddedTestServer::OCSPConfig::Produced::kValid), |
| OCSPVerifyResult::PROVIDED, OCSPRevocationStatus::GOOD}, |
| |
| // 14 |
| {EmbeddedTestServer::OCSPConfig( |
| {{OCSPRevocationStatus::GOOD, |
| EmbeddedTestServer::OCSPConfig::SingleResponse::Date::kEarly}, |
| {OCSPRevocationStatus::GOOD, |
| EmbeddedTestServer::OCSPConfig::SingleResponse::Date::kValid}}, |
| EmbeddedTestServer::OCSPConfig::Produced::kValid), |
| OCSPVerifyResult::PROVIDED, OCSPRevocationStatus::GOOD}, |
| |
| // 15 |
| {EmbeddedTestServer::OCSPConfig( |
| {{OCSPRevocationStatus::GOOD, |
| EmbeddedTestServer::OCSPConfig::SingleResponse::Date::kLong}, |
| {OCSPRevocationStatus::GOOD, |
| EmbeddedTestServer::OCSPConfig::SingleResponse::Date::kValid}}, |
| EmbeddedTestServer::OCSPConfig::Produced::kValid), |
| OCSPVerifyResult::PROVIDED, OCSPRevocationStatus::GOOD}, |
| |
| // 16 |
| {EmbeddedTestServer::OCSPConfig( |
| {{OCSPRevocationStatus::GOOD, |
| EmbeddedTestServer::OCSPConfig::SingleResponse::Date::kEarly}, |
| {OCSPRevocationStatus::GOOD, |
| EmbeddedTestServer::OCSPConfig::SingleResponse::Date::kOld}, |
| {OCSPRevocationStatus::GOOD, |
| EmbeddedTestServer::OCSPConfig::SingleResponse::Date::kLong}}, |
| EmbeddedTestServer::OCSPConfig::Produced::kValid), |
| OCSPVerifyResult::INVALID_DATE, OCSPRevocationStatus::UNKNOWN}, |
| |
| // 17 |
| {EmbeddedTestServer::OCSPConfig( |
| {{OCSPRevocationStatus::UNKNOWN, |
| EmbeddedTestServer::OCSPConfig::SingleResponse::Date::kValid}, |
| {OCSPRevocationStatus::REVOKED, |
| EmbeddedTestServer::OCSPConfig::SingleResponse::Date::kValid}, |
| {OCSPRevocationStatus::GOOD, |
| EmbeddedTestServer::OCSPConfig::SingleResponse::Date::kValid}}, |
| EmbeddedTestServer::OCSPConfig::Produced::kValid), |
| OCSPVerifyResult::PROVIDED, OCSPRevocationStatus::REVOKED}, |
| |
| // 18 |
| {EmbeddedTestServer::OCSPConfig( |
| {{OCSPRevocationStatus::UNKNOWN, |
| EmbeddedTestServer::OCSPConfig::SingleResponse::Date::kValid}, |
| {OCSPRevocationStatus::GOOD, |
| EmbeddedTestServer::OCSPConfig::SingleResponse::Date::kValid}}, |
| EmbeddedTestServer::OCSPConfig::Produced::kValid), |
| OCSPVerifyResult::PROVIDED, OCSPRevocationStatus::UNKNOWN}, |
| |
| // 19 |
| {EmbeddedTestServer::OCSPConfig( |
| {{OCSPRevocationStatus::UNKNOWN, |
| EmbeddedTestServer::OCSPConfig::SingleResponse::Date::kValid}, |
| {OCSPRevocationStatus::REVOKED, |
| EmbeddedTestServer::OCSPConfig::SingleResponse::Date::kLong}, |
| {OCSPRevocationStatus::GOOD, |
| EmbeddedTestServer::OCSPConfig::SingleResponse::Date::kValid}}, |
| EmbeddedTestServer::OCSPConfig::Produced::kValid), |
| OCSPVerifyResult::PROVIDED, OCSPRevocationStatus::UNKNOWN}, |
| |
| // 20 |
| {EmbeddedTestServer::OCSPConfig( |
| {{OCSPRevocationStatus::GOOD, |
| EmbeddedTestServer::OCSPConfig::SingleResponse::Date::kValid, |
| EmbeddedTestServer::OCSPConfig::SingleResponse::Serial::kMismatch}}, |
| EmbeddedTestServer::OCSPConfig::Produced::kValid), |
| OCSPVerifyResult::NO_MATCHING_RESPONSE, OCSPRevocationStatus::UNKNOWN}, |
| |
| // 21 |
| {EmbeddedTestServer::OCSPConfig( |
| {{OCSPRevocationStatus::GOOD, |
| EmbeddedTestServer::OCSPConfig::SingleResponse::Date::kEarly, |
| EmbeddedTestServer::OCSPConfig::SingleResponse::Serial::kMismatch}}, |
| EmbeddedTestServer::OCSPConfig::Produced::kValid), |
| OCSPVerifyResult::NO_MATCHING_RESPONSE, OCSPRevocationStatus::UNKNOWN}, |
| |
| // 22 |
| {EmbeddedTestServer::OCSPConfig( |
| {{OCSPRevocationStatus::REVOKED, |
| EmbeddedTestServer::OCSPConfig::SingleResponse::Date::kValid}}, |
| EmbeddedTestServer::OCSPConfig::Produced::kValid), |
| OCSPVerifyResult::PROVIDED, OCSPRevocationStatus::REVOKED}, |
| |
| // 23 |
| {EmbeddedTestServer::OCSPConfig( |
| {{OCSPRevocationStatus::REVOKED, |
| EmbeddedTestServer::OCSPConfig::SingleResponse::Date::kOld}}, |
| EmbeddedTestServer::OCSPConfig::Produced::kValid), |
| OCSPVerifyResult::INVALID_DATE, OCSPRevocationStatus::UNKNOWN}, |
| |
| // 24 |
| {EmbeddedTestServer::OCSPConfig( |
| {{OCSPRevocationStatus::REVOKED, |
| EmbeddedTestServer::OCSPConfig::SingleResponse::Date::kLong}}, |
| EmbeddedTestServer::OCSPConfig::Produced::kValid), |
| OCSPVerifyResult::INVALID_DATE, OCSPRevocationStatus::UNKNOWN}, |
| }; |
| |
| class HTTPSOCSPVerifyTest |
| : public HTTPSOCSPTest, |
| public testing::WithParamInterface<OCSPVerifyTestData> {}; |
| |
| TEST_P(HTTPSOCSPVerifyTest, VerifyResult) { |
| OCSPVerifyTestData test = GetParam(); |
| |
| scoped_refptr<X509Certificate> root_cert = |
| ImportCertFromFile(GetTestCertsDirectory(), "root_ca_cert.pem"); |
| ASSERT_TRUE(root_cert); |
| ScopedTestKnownRoot scoped_known_root(root_cert.get()); |
| |
| EmbeddedTestServer::ServerCertificateConfig cert_config; |
| cert_config.stapled_ocsp_config = test.ocsp_config; |
| cert_config.dns_names = {"example.com"}; |
| |
| SSLInfo ssl_info; |
| OCSPErrorTestDelegate delegate; |
| ASSERT_NO_FATAL_FAILURE(DoConnectionWithDelegate("example.com", cert_config, |
| &delegate, &ssl_info)); |
| |
| // The SSLInfo must be extracted from |delegate| on error, due to how |
| // URLRequest caches certificate errors. |
| if (delegate.have_certificate_errors()) { |
| ASSERT_TRUE(delegate.on_ssl_certificate_error_called()); |
| ssl_info = delegate.ssl_info(); |
| } |
| |
| EXPECT_EQ(test.expected_response_status, |
| ssl_info.ocsp_result.response_status); |
| |
| if (test.expected_response_status == OCSPVerifyResult::PROVIDED) { |
| EXPECT_EQ(test.expected_cert_status, |
| ssl_info.ocsp_result.revocation_status); |
| } |
| } |
| |
| INSTANTIATE_TEST_SUITE_P(OCSPVerify, |
| HTTPSOCSPVerifyTest, |
| testing::ValuesIn(kOCSPVerifyData)); |
| |
| class HTTPSAIATest : public HTTPSCertNetFetchingTest {}; |
| |
| TEST_F(HTTPSAIATest, AIAFetching) { |
| EmbeddedTestServer test_server(EmbeddedTestServer::TYPE_HTTPS); |
| EmbeddedTestServer::ServerCertificateConfig cert_config; |
| cert_config.intermediate = EmbeddedTestServer::IntermediateType::kByAIA; |
| test_server.SetSSLConfig(cert_config); |
| RegisterDefaultHandlers(&test_server); |
| ASSERT_TRUE(test_server.Start()); |
| |
| TestDelegate d; |
| d.set_allow_certificate_errors(true); |
| std::unique_ptr<URLRequest> r(context_->CreateRequest( |
| test_server.GetURL("/defaultresponse"), DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| |
| r->Start(); |
| EXPECT_TRUE(r->is_pending()); |
| |
| d.RunUntilComplete(); |
| |
| EXPECT_EQ(1, d.response_started_count()); |
| |
| CertStatus cert_status = r->ssl_info().cert_status; |
| EXPECT_EQ(OK, d.request_status()); |
| EXPECT_EQ(0u, cert_status & CERT_STATUS_ALL_ERRORS); |
| ASSERT_TRUE(r->ssl_info().cert); |
| EXPECT_EQ(2u, r->ssl_info().cert->intermediate_buffers().size()); |
| ASSERT_TRUE(r->ssl_info().unverified_cert); |
| EXPECT_EQ(0u, r->ssl_info().unverified_cert->intermediate_buffers().size()); |
| } |
| |
| class HTTPSHardFailTest : public HTTPSOCSPTest { |
| protected: |
| CertVerifier::Config GetCertVerifierConfig() override { |
| CertVerifier::Config config; |
| config.require_rev_checking_local_anchors = true; |
| return config; |
| } |
| }; |
| |
| TEST_F(HTTPSHardFailTest, Valid) { |
| if (!SystemSupportsOCSP()) { |
| LOG(WARNING) << "Skipping test because system doesn't support OCSP"; |
| return; |
| } |
| |
| if (!SystemSupportsHardFailRevocationChecking()) { |
| LOG(WARNING) << "Skipping test because system doesn't support hard fail " |
| << "revocation checking"; |
| return; |
| } |
| |
| EmbeddedTestServer::ServerCertificateConfig cert_config; |
| cert_config.ocsp_config = EmbeddedTestServer::OCSPConfig( |
| {{OCSPRevocationStatus::GOOD, |
| EmbeddedTestServer::OCSPConfig::SingleResponse::Date::kValid}}); |
| |
| CertStatus cert_status; |
| DoConnection(cert_config, &cert_status); |
| |
| EXPECT_EQ(0u, cert_status & CERT_STATUS_ALL_ERRORS); |
| EXPECT_TRUE(cert_status & CERT_STATUS_REV_CHECKING_ENABLED); |
| } |
| |
| TEST_F(HTTPSHardFailTest, Revoked) { |
| if (!SystemSupportsOCSP()) { |
| LOG(WARNING) << "Skipping test because system doesn't support OCSP"; |
| return; |
| } |
| |
| if (!SystemSupportsHardFailRevocationChecking()) { |
| LOG(WARNING) << "Skipping test because system doesn't support hard fail " |
| << "revocation checking"; |
| return; |
| } |
| |
| EmbeddedTestServer::ServerCertificateConfig cert_config; |
| cert_config.ocsp_config = EmbeddedTestServer::OCSPConfig( |
| {{OCSPRevocationStatus::REVOKED, |
| EmbeddedTestServer::OCSPConfig::SingleResponse::Date::kValid}}); |
| |
| CertStatus cert_status; |
| DoConnection(cert_config, &cert_status); |
| |
| EXPECT_EQ(CERT_STATUS_REVOKED, cert_status & CERT_STATUS_ALL_ERRORS); |
| EXPECT_FALSE(cert_status & CERT_STATUS_IS_EV); |
| EXPECT_TRUE(cert_status & CERT_STATUS_REV_CHECKING_ENABLED); |
| } |
| |
| TEST_F(HTTPSHardFailTest, FailsOnOCSPInvalid) { |
| if (!SystemSupportsOCSP()) { |
| LOG(WARNING) << "Skipping test because system doesn't support OCSP"; |
| return; |
| } |
| |
| if (!SystemSupportsHardFailRevocationChecking()) { |
| LOG(WARNING) << "Skipping test because system doesn't support hard fail " |
| << "revocation checking"; |
| return; |
| } |
| |
| EmbeddedTestServer::ServerCertificateConfig cert_config; |
| cert_config.ocsp_config = EmbeddedTestServer::OCSPConfig( |
| EmbeddedTestServer::OCSPConfig::ResponseType::kInvalidResponse); |
| |
| CertStatus cert_status; |
| DoConnection(cert_config, &cert_status); |
| |
| EXPECT_EQ(CERT_STATUS_UNABLE_TO_CHECK_REVOCATION, |
| cert_status & CERT_STATUS_ALL_ERRORS); |
| EXPECT_TRUE(cert_status & CERT_STATUS_REV_CHECKING_ENABLED); |
| } |
| |
| TEST_F(HTTPSHardFailTest, IntermediateResponseOldButStillValid) { |
| if (!SystemSupportsOCSP()) { |
| LOG(WARNING) << "Skipping test because system doesn't support OCSP"; |
| return; |
| } |
| |
| if (!SystemSupportsHardFailRevocationChecking()) { |
| LOG(WARNING) << "Skipping test because system doesn't support hard fail " |
| << "revocation checking"; |
| return; |
| } |
| |
| EmbeddedTestServer::ServerCertificateConfig cert_config; |
| cert_config.intermediate = EmbeddedTestServer::IntermediateType::kInHandshake; |
| cert_config.ocsp_config = EmbeddedTestServer::OCSPConfig( |
| {{OCSPRevocationStatus::GOOD, |
| EmbeddedTestServer::OCSPConfig::SingleResponse::Date::kValid}}); |
| // Use an OCSP response for the intermediate that would be too old for a leaf |
| // cert, but is still valid for an intermediate. |
| cert_config.intermediate_ocsp_config = EmbeddedTestServer::OCSPConfig( |
| {{OCSPRevocationStatus::GOOD, |
| EmbeddedTestServer::OCSPConfig::SingleResponse::Date::kLong}}); |
| |
| CertStatus cert_status; |
| DoConnection(cert_config, &cert_status); |
| |
| EXPECT_EQ(0u, cert_status & CERT_STATUS_ALL_ERRORS); |
| EXPECT_TRUE(cert_status & CERT_STATUS_REV_CHECKING_ENABLED); |
| } |
| |
| TEST_F(HTTPSHardFailTest, IntermediateResponseTooOld) { |
| if (!SystemSupportsOCSP()) { |
| LOG(WARNING) << "Skipping test because system doesn't support OCSP"; |
| return; |
| } |
| |
| if (!SystemSupportsHardFailRevocationChecking()) { |
| LOG(WARNING) << "Skipping test because system doesn't support hard fail " |
| << "revocation checking"; |
| return; |
| } |
| |
| EmbeddedTestServer::ServerCertificateConfig cert_config; |
| cert_config.intermediate = EmbeddedTestServer::IntermediateType::kInHandshake; |
| cert_config.ocsp_config = EmbeddedTestServer::OCSPConfig( |
| {{OCSPRevocationStatus::GOOD, |
| EmbeddedTestServer::OCSPConfig::SingleResponse::Date::kValid}}); |
| // Use an OCSP response for the intermediate that is too old according to |
| // BRs, but is fine for a locally trusted root. |
| cert_config.intermediate_ocsp_config = EmbeddedTestServer::OCSPConfig( |
| {{OCSPRevocationStatus::GOOD, |
| EmbeddedTestServer::OCSPConfig::SingleResponse::Date::kLonger}}); |
| |
| CertStatus cert_status; |
| DoConnection(cert_config, &cert_status); |
| |
| EXPECT_EQ(0u, cert_status & CERT_STATUS_ALL_ERRORS); |
| EXPECT_TRUE(cert_status & CERT_STATUS_REV_CHECKING_ENABLED); |
| } |
| |
| TEST_F(HTTPSHardFailTest, ValidStapled) { |
| if (!SystemSupportsOCSPStapling()) { |
| LOG(WARNING) |
| << "Skipping test because system doesn't support OCSP stapling"; |
| return; |
| } |
| |
| if (!SystemSupportsHardFailRevocationChecking()) { |
| LOG(WARNING) << "Skipping test because system doesn't support hard fail " |
| << "revocation checking"; |
| return; |
| } |
| |
| EmbeddedTestServer::ServerCertificateConfig cert_config; |
| |
| // AIA OCSP url is included, but does not return a successful ocsp response. |
| cert_config.ocsp_config = EmbeddedTestServer::OCSPConfig( |
| EmbeddedTestServer::OCSPConfig::ResponseType::kTryLater); |
| |
| cert_config.stapled_ocsp_config = EmbeddedTestServer::OCSPConfig( |
| {{OCSPRevocationStatus::GOOD, |
| EmbeddedTestServer::OCSPConfig::SingleResponse::Date::kValid}}); |
| |
| CertStatus cert_status; |
| DoConnection(cert_config, &cert_status); |
| |
| EXPECT_EQ(0u, cert_status & CERT_STATUS_ALL_ERRORS); |
| EXPECT_TRUE(cert_status & CERT_STATUS_REV_CHECKING_ENABLED); |
| } |
| |
| TEST_F(HTTPSHardFailTest, RevokedStapled) { |
| if (!SystemSupportsOCSPStapling()) { |
| LOG(WARNING) |
| << "Skipping test because system doesn't support OCSP stapling"; |
| return; |
| } |
| |
| if (!SystemSupportsHardFailRevocationChecking()) { |
| LOG(WARNING) << "Skipping test because system doesn't support hard fail " |
| << "revocation checking"; |
| return; |
| } |
| |
| EmbeddedTestServer::ServerCertificateConfig cert_config; |
| |
| // AIA OCSP url is included, but does not return a successful ocsp response. |
| cert_config.ocsp_config = EmbeddedTestServer::OCSPConfig( |
| EmbeddedTestServer::OCSPConfig::ResponseType::kTryLater); |
| |
| cert_config.stapled_ocsp_config = EmbeddedTestServer::OCSPConfig( |
| {{OCSPRevocationStatus::REVOKED, |
| EmbeddedTestServer::OCSPConfig::SingleResponse::Date::kValid}}); |
| |
| CertStatus cert_status; |
| DoConnection(cert_config, &cert_status); |
| |
| EXPECT_EQ(CERT_STATUS_REVOKED, cert_status & CERT_STATUS_ALL_ERRORS); |
| EXPECT_TRUE(cert_status & CERT_STATUS_REV_CHECKING_ENABLED); |
| } |
| |
| TEST_F(HTTPSHardFailTest, OldStapledAndInvalidAIA) { |
| if (!SystemSupportsOCSPStapling()) { |
| LOG(WARNING) |
| << "Skipping test because system doesn't support OCSP stapling"; |
| return; |
| } |
| |
| if (!SystemSupportsHardFailRevocationChecking()) { |
| LOG(WARNING) << "Skipping test because system doesn't support hard fail " |
| << "revocation checking"; |
| return; |
| } |
| |
| EmbeddedTestServer::ServerCertificateConfig cert_config; |
| |
| // Stapled response indicates good, but is too old. |
| cert_config.stapled_ocsp_config = EmbeddedTestServer::OCSPConfig( |
| {{OCSPRevocationStatus::GOOD, |
| EmbeddedTestServer::OCSPConfig::SingleResponse::Date::kOld}}); |
| |
| // AIA OCSP url is included, but does not return a successful ocsp response. |
| cert_config.ocsp_config = EmbeddedTestServer::OCSPConfig( |
| EmbeddedTestServer::OCSPConfig::ResponseType::kTryLater); |
| |
| CertStatus cert_status; |
| DoConnection(cert_config, &cert_status); |
| |
| EXPECT_EQ(CERT_STATUS_UNABLE_TO_CHECK_REVOCATION, |
| cert_status & CERT_STATUS_ALL_ERRORS); |
| EXPECT_TRUE(cert_status & CERT_STATUS_REV_CHECKING_ENABLED); |
| } |
| |
| TEST_F(HTTPSHardFailTest, OldStapledButValidAIA) { |
| if (!SystemSupportsOCSPStapling()) { |
| LOG(WARNING) |
| << "Skipping test because system doesn't support OCSP stapling"; |
| return; |
| } |
| |
| if (!SystemSupportsHardFailRevocationChecking()) { |
| LOG(WARNING) << "Skipping test because system doesn't support hard fail " |
| << "revocation checking"; |
| return; |
| } |
| |
| EmbeddedTestServer::ServerCertificateConfig cert_config; |
| |
| // Stapled response indicates good, but response is too old. |
| cert_config.stapled_ocsp_config = EmbeddedTestServer::OCSPConfig( |
| {{OCSPRevocationStatus::GOOD, |
| EmbeddedTestServer::OCSPConfig::SingleResponse::Date::kOld}}); |
| |
| // AIA OCSP url is included, and returns a successful ocsp response. |
| cert_config.ocsp_config = EmbeddedTestServer::OCSPConfig( |
| {{OCSPRevocationStatus::GOOD, |
| EmbeddedTestServer::OCSPConfig::SingleResponse::Date::kValid}}); |
| |
| CertStatus cert_status; |
| DoConnection(cert_config, &cert_status); |
| |
| EXPECT_EQ(0u, cert_status & CERT_STATUS_ALL_ERRORS); |
| EXPECT_TRUE(cert_status & CERT_STATUS_REV_CHECKING_ENABLED); |
| } |
| |
| class HTTPSCRLSetTest : public HTTPSCertNetFetchingTest {}; |
| |
| TEST_F(HTTPSCRLSetTest, ExpiredCRLSet) { |
| EmbeddedTestServer::ServerCertificateConfig cert_config; |
| cert_config.ocsp_config = EmbeddedTestServer::OCSPConfig( |
| EmbeddedTestServer::OCSPConfig::ResponseType::kInvalidResponse); |
| |
| UpdateCertVerifier(CRLSet::ExpiredCRLSetForTesting()); |
| |
| CertStatus cert_status; |
| DoConnection(cert_config, &cert_status); |
| |
| // If we're not trying EV verification then, even if the CRLSet has expired, |
| // we don't fall back to online revocation checks. |
| EXPECT_EQ(0u, cert_status & CERT_STATUS_ALL_ERRORS); |
| EXPECT_FALSE(cert_status & CERT_STATUS_IS_EV); |
| EXPECT_FALSE(cert_status & CERT_STATUS_REV_CHECKING_ENABLED); |
| } |
| |
| TEST_F(HTTPSCRLSetTest, ExpiredCRLSetAndRevoked) { |
| // Test that when online revocation checking is disabled, and the leaf |
| // certificate is not EV, that no revocation checking actually happens. |
| if (!SystemSupportsOCSP()) { |
| LOG(WARNING) << "Skipping test because system doesn't support OCSP"; |
| return; |
| } |
| |
| EmbeddedTestServer::ServerCertificateConfig cert_config; |
| cert_config.ocsp_config = EmbeddedTestServer::OCSPConfig( |
| {{OCSPRevocationStatus::REVOKED, |
| EmbeddedTestServer::OCSPConfig::SingleResponse::Date::kValid}}); |
| |
| UpdateCertVerifier(CRLSet::ExpiredCRLSetForTesting()); |
| |
| CertStatus cert_status; |
| DoConnection(cert_config, &cert_status); |
| |
| EXPECT_EQ(0u, cert_status & CERT_STATUS_ALL_ERRORS); |
| |
| EXPECT_FALSE(cert_status & CERT_STATUS_IS_EV); |
| EXPECT_FALSE(cert_status & CERT_STATUS_REV_CHECKING_ENABLED); |
| } |
| |
| TEST_F(HTTPSCRLSetTest, CRLSetRevoked) { |
| if (!SystemSupportsCRLSets()) { |
| LOG(WARNING) << "Skipping test because system doesn't support CRLSets"; |
| return; |
| } |
| |
| EmbeddedTestServer test_server(EmbeddedTestServer::TYPE_HTTPS); |
| EmbeddedTestServer::ServerCertificateConfig cert_config; |
| cert_config.ocsp_config = EmbeddedTestServer::OCSPConfig( |
| {{OCSPRevocationStatus::GOOD, |
| EmbeddedTestServer::OCSPConfig::SingleResponse::Date::kValid}}); |
| test_server.SetSSLConfig(cert_config); |
| RegisterDefaultHandlers(&test_server); |
| ASSERT_TRUE(test_server.Start()); |
| |
| CertVerifier::Config cert_verifier_config = GetCertVerifierConfig(); |
| SHA256HashValue root_cert_spki_hash; |
| ASSERT_TRUE(GetTestRootCertSPKIHash(&root_cert_spki_hash)); |
| auto crl_set = |
| CRLSet::ForTesting(false, &root_cert_spki_hash, |
| test_server.GetCertificate()->serial_number(), "", {}); |
| ASSERT_TRUE(crl_set); |
| UpdateCertVerifier(crl_set); |
| |
| TestDelegate d; |
| d.set_allow_certificate_errors(true); |
| std::unique_ptr<URLRequest> r(context_->CreateRequest( |
| test_server.GetURL("/defaultresponse"), DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| r->Start(); |
| EXPECT_TRUE(r->is_pending()); |
| d.RunUntilComplete(); |
| EXPECT_EQ(1, d.response_started_count()); |
| CertStatus cert_status = r->ssl_info().cert_status; |
| |
| // If the certificate is recorded as revoked in the CRLSet, that should be |
| // reflected without online revocation checking. |
| EXPECT_EQ(CERT_STATUS_REVOKED, cert_status & CERT_STATUS_ALL_ERRORS); |
| EXPECT_FALSE(cert_status & CERT_STATUS_IS_EV); |
| EXPECT_FALSE(cert_status & CERT_STATUS_REV_CHECKING_ENABLED); |
| } |
| |
| TEST_F(HTTPSCRLSetTest, CRLSetRevokedBySubject) { |
| if (!SystemSupportsCRLSets()) { |
| LOG(WARNING) << "Skipping test because system doesn't support CRLSets"; |
| return; |
| } |
| |
| EmbeddedTestServer test_server(EmbeddedTestServer::TYPE_HTTPS); |
| EmbeddedTestServer::ServerCertificateConfig cert_config; |
| cert_config.ocsp_config = EmbeddedTestServer::OCSPConfig( |
| {{OCSPRevocationStatus::GOOD, |
| EmbeddedTestServer::OCSPConfig::SingleResponse::Date::kValid}}); |
| test_server.SetSSLConfig(cert_config); |
| RegisterDefaultHandlers(&test_server); |
| ASSERT_TRUE(test_server.Start()); |
| |
| std::string common_name = test_server.GetCertificate()->subject().common_name; |
| |
| { |
| auto crl_set = CRLSet::ForTesting(false, nullptr, "", common_name, {}); |
| ASSERT_TRUE(crl_set); |
| UpdateCertVerifier(crl_set); |
| |
| TestDelegate d; |
| d.set_allow_certificate_errors(true); |
| std::unique_ptr<URLRequest> r(context_->CreateRequest( |
| test_server.GetURL("/defaultresponse"), DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| r->Start(); |
| EXPECT_TRUE(r->is_pending()); |
| d.RunUntilComplete(); |
| EXPECT_EQ(1, d.response_started_count()); |
| CertStatus cert_status = r->ssl_info().cert_status; |
| |
| // If the certificate is recorded as revoked in the CRLSet, that should be |
| // reflected without online revocation checking. |
| EXPECT_EQ(CERT_STATUS_REVOKED, cert_status & CERT_STATUS_ALL_ERRORS); |
| EXPECT_FALSE(cert_status & CERT_STATUS_IS_EV); |
| EXPECT_FALSE(cert_status & CERT_STATUS_REV_CHECKING_ENABLED); |
| } |
| |
| HashValue spki_hash_value; |
| ASSERT_TRUE(x509_util::CalculateSha256SpkiHash( |
| test_server.GetCertificate()->cert_buffer(), &spki_hash_value)); |
| std::string spki_hash(spki_hash_value.data(), |
| spki_hash_value.data() + spki_hash_value.size()); |
| { |
| auto crl_set = |
| CRLSet::ForTesting(false, nullptr, "", common_name, {spki_hash}); |
| ASSERT_TRUE(crl_set); |
| UpdateCertVerifier(crl_set); |
| |
| TestDelegate d; |
| d.set_allow_certificate_errors(true); |
| std::unique_ptr<URLRequest> r(context_->CreateRequest( |
| test_server.GetURL("/defaultresponse"), DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| r->Start(); |
| EXPECT_TRUE(r->is_pending()); |
| d.RunUntilComplete(); |
| EXPECT_EQ(1, d.response_started_count()); |
| CertStatus cert_status = r->ssl_info().cert_status; |
| |
| // When the correct SPKI hash is specified in |
| // |acceptable_spki_hashes_for_cn|, the connection should succeed even |
| // though the subject is listed in the CRLSet. |
| EXPECT_EQ(0u, cert_status & CERT_STATUS_ALL_ERRORS); |
| } |
| } |
| |
| using HTTPSLocalCRLSetTest = TestWithTaskEnvironment; |
| |
| // Use a real CertVerifier to attempt to connect to the TestServer, and ensure |
| // that when a CRLSet is provided that marks a given SPKI (the TestServer's |
| // root SPKI) as known for interception, that it's adequately flagged. |
| TEST_F(HTTPSLocalCRLSetTest, KnownInterceptionBlocked) { |
| auto cert_verifier = CertVerifier::CreateDefaultWithoutCaching( |
| /*cert_net_fetcher=*/nullptr); |
| CertVerifierWithUpdatableProc* updatable_cert_verifier_ = cert_verifier.get(); |
| |
| auto context_builder = CreateTestURLRequestContextBuilder(); |
| context_builder->SetCertVerifier(std::make_unique<CachingCertVerifier>( |
| std::make_unique<CoalescingCertVerifier>(std::move(cert_verifier)))); |
| auto context = context_builder->Build(); |
| |
| // Verify the connection succeeds without being flagged. |
| EmbeddedTestServer https_server(EmbeddedTestServer::TYPE_HTTPS); |
| RegisterDefaultHandlers(&https_server); |
| https_server.SetSSLConfig(EmbeddedTestServer::CERT_OK_BY_INTERMEDIATE); |
| ASSERT_TRUE(https_server.Start()); |
| |
| { |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req( |
| context->CreateRequest(https_server.GetURL("/"), DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->Start(); |
| d.RunUntilComplete(); |
| |
| EXPECT_EQ(1, d.response_started_count()); |
| EXPECT_FALSE(d.request_failed()); |
| EXPECT_FALSE(d.have_certificate_errors()); |
| EXPECT_FALSE(req->ssl_info().cert_status & |
| CERT_STATUS_KNOWN_INTERCEPTION_BLOCKED); |
| } |
| |
| // Configure a CRL that will mark |root_ca_cert| as a blocked interception |
| // root. |
| std::string crl_set_bytes; |
| net::CertVerifyProcFactory::ImplParams params; |
| ASSERT_TRUE( |
| base::ReadFileToString(GetTestCertsDirectory().AppendASCII( |
| "crlset_blocked_interception_by_root.raw"), |
| &crl_set_bytes)); |
| ASSERT_TRUE(CRLSet::Parse(crl_set_bytes, ¶ms.crl_set)); |
| |
| updatable_cert_verifier_->UpdateVerifyProcData( |
| /*cert_net_fetcher=*/nullptr, params); |
| |
| // Verify the connection fails as being a known interception root. |
| { |
| TestDelegate d; |
| d.set_allow_certificate_errors(true); |
| std::unique_ptr<URLRequest> req( |
| context->CreateRequest(https_server.GetURL("/"), DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->Start(); |
| d.RunUntilComplete(); |
| |
| EXPECT_EQ(1, d.response_started_count()); |
| EXPECT_FALSE(d.request_failed()); |
| if (SystemSupportsCRLSets()) { |
| EXPECT_TRUE(d.have_certificate_errors()); |
| EXPECT_FALSE(d.certificate_errors_are_fatal()); |
| EXPECT_EQ(ERR_CERT_KNOWN_INTERCEPTION_BLOCKED, d.certificate_net_error()); |
| EXPECT_TRUE(req->ssl_info().cert_status & |
| CERT_STATUS_KNOWN_INTERCEPTION_BLOCKED); |
| } else { |
| EXPECT_FALSE(d.have_certificate_errors()); |
| EXPECT_TRUE(req->ssl_info().cert_status & |
| CERT_STATUS_KNOWN_INTERCEPTION_DETECTED); |
| } |
| } |
| } |
| |
| TEST_F(HTTPSLocalCRLSetTest, InterceptionBlockedAllowOverrideOnHSTS) { |
| constexpr char kHSTSHost[] = "include-subdomains-hsts-preloaded.test"; |
| constexpr char kHSTSSubdomainWithKnownInterception[] = |
| "www.include-subdomains-hsts-preloaded.test"; |
| |
| EmbeddedTestServer https_server(net::EmbeddedTestServer::TYPE_HTTPS); |
| https_server.SetSSLConfig(net::EmbeddedTestServer::CERT_OK_BY_INTERMEDIATE); |
| https_server.ServeFilesFromSourceDirectory(base::FilePath(kTestFilePath)); |
| ASSERT_TRUE(https_server.Start()); |
| |
| // Configure the CertVerifier to simulate: |
| // - For the test server host, that the certificate is issued by an |
| // unknown authority; this SHOULD NOT be a fatal error when signaled |
| // to the delegate. |
| // - For |kHSTSHost|, that the certificate is issued by an unknown |
| // authority; this SHOULD be a fatal error. |
| // Combined, these two states represent the baseline: non-fatal for non-HSTS |
| // hosts, fatal for HSTS host. |
| // - For |kHSTSSubdomainWithKnownInterception|, that the certificate is |
| // issued by a known interception cert. This SHOULD be an error, but |
| // SHOULD NOT be a fatal error |
| auto cert_verifier = std::make_unique<MockCertVerifier>(); |
| |
| scoped_refptr<X509Certificate> cert = https_server.GetCertificate(); |
| ASSERT_TRUE(cert); |
| |
| HashValue filler_hash; |
| ASSERT_TRUE(filler_hash.FromString( |
| "sha256/3333333333333333333333333333333333333333333=")); |
| |
| CertVerifyResult fake_result; |
| fake_result.verified_cert = cert; |
| fake_result.is_issued_by_known_root = false; |
| |
| // Configure for the test server's default host. |
| CertVerifyResult test_result = fake_result; |
| test_result.public_key_hashes.push_back(filler_hash); |
| test_result.cert_status |= CERT_STATUS_AUTHORITY_INVALID; |
| cert_verifier->AddResultForCertAndHost( |
| cert.get(), https_server.host_port_pair().host(), test_result, |
| ERR_CERT_AUTHORITY_INVALID); |
| |
| // Configure for kHSTSHost. |
| CertVerifyResult sts_base_result = fake_result; |
| sts_base_result.public_key_hashes.push_back(filler_hash); |
| sts_base_result.cert_status |= CERT_STATUS_AUTHORITY_INVALID; |
| cert_verifier->AddResultForCertAndHost(cert.get(), kHSTSHost, sts_base_result, |
| ERR_CERT_AUTHORITY_INVALID); |
| |
| // Configure for kHSTSSubdomainWithKnownInterception |
| CertVerifyResult sts_sub_result = fake_result; |
| SHA256HashValue root_hash; |
| ASSERT_TRUE(GetTestRootCertSPKIHash(&root_hash)); |
| sts_sub_result.public_key_hashes.push_back(HashValue(root_hash)); |
| sts_sub_result.cert_status |= |
| CERT_STATUS_REVOKED | CERT_STATUS_KNOWN_INTERCEPTION_BLOCKED; |
| cert_verifier->AddResultForCertAndHost( |
| cert.get(), kHSTSSubdomainWithKnownInterception, sts_sub_result, |
| ERR_CERT_KNOWN_INTERCEPTION_BLOCKED); |
| |
| // Configure the initial context. |
| auto context_builder = CreateTestURLRequestContextBuilder(); |
| context_builder->SetCertVerifier(std::move(cert_verifier)); |
| auto context = context_builder->Build(); |
| |
| // Enable preloaded HSTS for |kHSTSHost|. |
| ASSERT_TRUE(context->transport_security_state()); |
| TransportSecurityState& security_state = *context->transport_security_state(); |
| security_state.EnableStaticPinsForTesting(); |
| security_state.SetPinningListAlwaysTimelyForTesting(true); |
| SetTransportSecurityStateSourceForTesting(&test_default::kHSTSSource); |
| |
| // Connect to the test server and see the certificate error flagged, but |
| // not fatal. |
| { |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req( |
| context->CreateRequest(https_server.GetURL("/"), DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->Start(); |
| d.RunUntilComplete(); |
| |
| EXPECT_EQ(1, d.response_started_count()); |
| EXPECT_TRUE(d.request_failed()); |
| EXPECT_TRUE(d.have_certificate_errors()); |
| EXPECT_FALSE(d.certificate_errors_are_fatal()); |
| EXPECT_FALSE(req->ssl_info().cert_status & |
| CERT_STATUS_KNOWN_INTERCEPTION_BLOCKED); |
| } |
| |
| // Connect to kHSTSHost and see the certificate errors are flagged, and are |
| // fatal. |
| { |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req(context->CreateRequest( |
| https_server.GetURL(kHSTSHost, "/"), DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->Start(); |
| d.RunUntilComplete(); |
| |
| EXPECT_EQ(1, d.response_started_count()); |
| EXPECT_TRUE(d.request_failed()); |
| EXPECT_TRUE(d.have_certificate_errors()); |
| EXPECT_TRUE(d.certificate_errors_are_fatal()); |
| EXPECT_FALSE(req->ssl_info().cert_status & |
| CERT_STATUS_KNOWN_INTERCEPTION_BLOCKED); |
| } |
| |
| // Verify the connection fails as being a known interception root. |
| { |
| TestDelegate d; |
| d.set_allow_certificate_errors(true); |
| std::unique_ptr<URLRequest> req(context->CreateRequest( |
| https_server.GetURL(kHSTSSubdomainWithKnownInterception, "/"), |
| DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->Start(); |
| d.RunUntilComplete(); |
| |
| EXPECT_EQ(1, d.response_started_count()); |
| EXPECT_FALSE(d.request_failed()); |
| EXPECT_TRUE(d.have_certificate_errors()); |
| EXPECT_FALSE(d.certificate_errors_are_fatal()); |
| EXPECT_EQ(ERR_CERT_KNOWN_INTERCEPTION_BLOCKED, d.certificate_net_error()); |
| EXPECT_TRUE(req->ssl_info().cert_status & |
| CERT_STATUS_KNOWN_INTERCEPTION_BLOCKED); |
| } |
| } |
| #endif // !BUILDFLAG(IS_IOS) |
| |
| TEST_F(URLRequestTest, NetworkAccessedSetOnHostResolutionFailure) { |
| auto context_builder = CreateTestURLRequestContextBuilder(); |
| auto host_resolver = std::make_unique<MockHostResolver>(); |
| host_resolver->rules()->AddSimulatedTimeoutFailure("*"); |
| context_builder->set_host_resolver(std::move(host_resolver)); |
| auto context = context_builder->Build(); |
| |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req(context->CreateRequest( |
| GURL("http://test_intercept/foo"), DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| |
| EXPECT_FALSE(req->response_info().network_accessed); |
| |
| req->Start(); |
| d.RunUntilComplete(); |
| EXPECT_TRUE(req->response_info().network_accessed); |
| EXPECT_THAT(req->response_info().resolve_error_info.error, |
| IsError(ERR_DNS_TIMED_OUT)); |
| } |
| |
| // Test that URLRequest is canceled correctly. |
| // See http://crbug.com/508900 |
| TEST_F(URLRequestTest, URLRequestRedirectJobCancelRequest) { |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req(default_context().CreateRequest( |
| GURL("http://not-a-real-domain/"), DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| |
| std::unique_ptr<URLRequestRedirectJob> job = |
| std::make_unique<URLRequestRedirectJob>( |
| req.get(), GURL("http://this-should-never-be-navigated-to/"), |
| RedirectUtil::ResponseCode::REDIRECT_307_TEMPORARY_REDIRECT, |
| "Jumbo shrimp"); |
| TestScopedURLInterceptor interceptor(req->url(), std::move(job)); |
| |
| req->Start(); |
| req->Cancel(); |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_EQ(ERR_ABORTED, d.request_status()); |
| EXPECT_EQ(0, d.received_redirect_count()); |
| } |
| |
| #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_CHROMEOS) |
| #define MAYBE_HeadersCallbacks DISABLED_HeadersCallbacks |
| #else |
| #define MAYBE_HeadersCallbacks HeadersCallbacks |
| #endif |
| TEST_F(URLRequestTestHTTP, MAYBE_HeadersCallbacks) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| GURL url(http_test_server()->GetURL("/cachetime")); |
| TestDelegate delegate; |
| HttpRequestHeaders extra_headers; |
| extra_headers.SetHeader("X-Foo", "bar"); |
| |
| { |
| HttpRawRequestHeaders raw_req_headers; |
| scoped_refptr<const HttpResponseHeaders> raw_resp_headers; |
| |
| std::unique_ptr<URLRequest> r(default_context().CreateRequest( |
| url, DEFAULT_PRIORITY, &delegate, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| r->SetExtraRequestHeaders(extra_headers); |
| r->SetRequestHeadersCallback(base::BindRepeating( |
| &HttpRawRequestHeaders::Assign, base::Unretained(&raw_req_headers))); |
| r->SetResponseHeadersCallback(base::BindRepeating( |
| [](scoped_refptr<const HttpResponseHeaders>* left, |
| scoped_refptr<const HttpResponseHeaders> right) { *left = right; }, |
| base::Unretained(&raw_resp_headers))); |
| r->set_isolation_info(isolation_info1_); |
| r->Start(); |
| while (!delegate.response_started_count()) |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_FALSE(raw_req_headers.headers().empty()); |
| std::string value; |
| EXPECT_TRUE(raw_req_headers.FindHeaderForTest("X-Foo", &value)); |
| EXPECT_EQ("bar", value); |
| EXPECT_TRUE(raw_req_headers.FindHeaderForTest("Accept-Encoding", &value)); |
| EXPECT_EQ("gzip, deflate", value); |
| EXPECT_TRUE(raw_req_headers.FindHeaderForTest("Connection", &value)); |
| EXPECT_TRUE(raw_req_headers.FindHeaderForTest("Host", &value)); |
| EXPECT_EQ("GET /cachetime HTTP/1.1\r\n", raw_req_headers.request_line()); |
| EXPECT_EQ(raw_resp_headers.get(), r->response_headers()); |
| } |
| { |
| std::unique_ptr<URLRequest> r(default_context().CreateRequest( |
| url, DEFAULT_PRIORITY, &delegate, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| r->SetExtraRequestHeaders(extra_headers); |
| r->SetRequestHeadersCallback(base::BindRepeating([](HttpRawRequestHeaders) { |
| FAIL() << "Callback should not be called unless request is sent"; |
| })); |
| r->SetResponseHeadersCallback( |
| base::BindRepeating([](scoped_refptr<const HttpResponseHeaders>) { |
| FAIL() << "Callback should not be called unless request is sent"; |
| })); |
| r->set_isolation_info(isolation_info1_); |
| r->Start(); |
| delegate.RunUntilComplete(); |
| EXPECT_TRUE(r->was_cached()); |
| } |
| } |
| |
| TEST_F(URLRequestTestHTTP, HeadersCallbacksWithRedirect) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| HttpRawRequestHeaders raw_req_headers; |
| scoped_refptr<const HttpResponseHeaders> raw_resp_headers; |
| |
| TestDelegate delegate; |
| HttpRequestHeaders extra_headers; |
| extra_headers.SetHeader("X-Foo", "bar"); |
| GURL url(http_test_server()->GetURL("/redirect-test.html")); |
| std::unique_ptr<URLRequest> r(default_context().CreateRequest( |
| url, DEFAULT_PRIORITY, &delegate, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| r->SetExtraRequestHeaders(extra_headers); |
| r->SetRequestHeadersCallback(base::BindRepeating( |
| &HttpRawRequestHeaders::Assign, base::Unretained(&raw_req_headers))); |
| r->SetResponseHeadersCallback(base::BindRepeating( |
| [](scoped_refptr<const HttpResponseHeaders>* left, |
| scoped_refptr<const HttpResponseHeaders> right) { *left = right; }, |
| base::Unretained(&raw_resp_headers))); |
| r->Start(); |
| delegate.RunUntilRedirect(); |
| |
| ASSERT_EQ(1, delegate.received_redirect_count()); |
| std::string value; |
| EXPECT_TRUE(raw_req_headers.FindHeaderForTest("X-Foo", &value)); |
| EXPECT_EQ("bar", value); |
| EXPECT_TRUE(raw_req_headers.FindHeaderForTest("Accept-Encoding", &value)); |
| EXPECT_EQ("gzip, deflate", value); |
| EXPECT_EQ(1, delegate.received_redirect_count()); |
| EXPECT_EQ("GET /redirect-test.html HTTP/1.1\r\n", |
| raw_req_headers.request_line()); |
| EXPECT_TRUE(raw_resp_headers->HasHeader("Location")); |
| EXPECT_EQ(302, raw_resp_headers->response_code()); |
| EXPECT_EQ("Redirect", raw_resp_headers->GetStatusText()); |
| |
| raw_req_headers = HttpRawRequestHeaders(); |
| raw_resp_headers = nullptr; |
| r->FollowDeferredRedirect(absl::nullopt /* removed_headers */, |
| absl::nullopt /* modified_headers */); |
| delegate.RunUntilComplete(); |
| EXPECT_TRUE(raw_req_headers.FindHeaderForTest("X-Foo", &value)); |
| EXPECT_EQ("bar", value); |
| EXPECT_TRUE(raw_req_headers.FindHeaderForTest("Accept-Encoding", &value)); |
| EXPECT_EQ("gzip, deflate", value); |
| EXPECT_EQ("GET /with-headers.html HTTP/1.1\r\n", |
| raw_req_headers.request_line()); |
| EXPECT_EQ(r->response_headers(), raw_resp_headers.get()); |
| } |
| |
| TEST_F(URLRequestTest, HeadersCallbacksConnectFailed) { |
| TestDelegate request_delegate; |
| |
| std::unique_ptr<URLRequest> r(default_context().CreateRequest( |
| GURL("http://127.0.0.1:9/"), DEFAULT_PRIORITY, &request_delegate, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| r->SetRequestHeadersCallback( |
| base::BindRepeating([](net::HttpRawRequestHeaders) { |
| FAIL() << "Callback should not be called unless request is sent"; |
| })); |
| r->SetResponseHeadersCallback( |
| base::BindRepeating([](scoped_refptr<const net::HttpResponseHeaders>) { |
| FAIL() << "Callback should not be called unless request is sent"; |
| })); |
| r->Start(); |
| request_delegate.RunUntilComplete(); |
| EXPECT_FALSE(r->is_pending()); |
| } |
| |
| TEST_F(URLRequestTestHTTP, HeadersCallbacksAuthRetry) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| GURL url(http_test_server()->GetURL("/auth-basic")); |
| |
| TestDelegate delegate; |
| |
| delegate.set_credentials(AuthCredentials(kUser, kSecret)); |
| HttpRequestHeaders extra_headers; |
| extra_headers.SetHeader("X-Foo", "bar"); |
| |
| using ReqHeadersVector = std::vector<std::unique_ptr<HttpRawRequestHeaders>>; |
| ReqHeadersVector raw_req_headers; |
| |
| using RespHeadersVector = |
| std::vector<scoped_refptr<const HttpResponseHeaders>>; |
| RespHeadersVector raw_resp_headers; |
| |
| auto req_headers_callback = base::BindRepeating( |
| [](ReqHeadersVector* vec, HttpRawRequestHeaders headers) { |
| vec->emplace_back( |
| std::make_unique<HttpRawRequestHeaders>(std::move(headers))); |
| }, |
| &raw_req_headers); |
| auto resp_headers_callback = base::BindRepeating( |
| [](RespHeadersVector* vec, |
| scoped_refptr<const HttpResponseHeaders> headers) { |
| vec->push_back(headers); |
| }, |
| &raw_resp_headers); |
| std::unique_ptr<URLRequest> r(default_context().CreateRequest( |
| url, DEFAULT_PRIORITY, &delegate, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| r->SetExtraRequestHeaders(extra_headers); |
| r->SetRequestHeadersCallback(req_headers_callback); |
| r->SetResponseHeadersCallback(resp_headers_callback); |
| r->set_isolation_info(isolation_info1_); |
| r->Start(); |
| delegate.RunUntilComplete(); |
| EXPECT_FALSE(r->is_pending()); |
| ASSERT_EQ(raw_req_headers.size(), 2u); |
| ASSERT_EQ(raw_resp_headers.size(), 2u); |
| std::string value; |
| EXPECT_FALSE(raw_req_headers[0]->FindHeaderForTest("Authorization", &value)); |
| EXPECT_TRUE(raw_req_headers[0]->FindHeaderForTest("X-Foo", &value)); |
| EXPECT_EQ("bar", value); |
| EXPECT_TRUE(raw_req_headers[1]->FindHeaderForTest("Authorization", &value)); |
| EXPECT_TRUE(raw_req_headers[1]->FindHeaderForTest("X-Foo", &value)); |
| EXPECT_EQ("bar", value); |
| EXPECT_EQ(raw_resp_headers[1], r->response_headers()); |
| EXPECT_NE(raw_resp_headers[0], raw_resp_headers[1]); |
| EXPECT_EQ(401, raw_resp_headers[0]->response_code()); |
| EXPECT_EQ("Unauthorized", raw_resp_headers[0]->GetStatusText()); |
| |
| std::unique_ptr<URLRequest> r2(default_context().CreateRequest( |
| url, DEFAULT_PRIORITY, &delegate, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| r2->SetExtraRequestHeaders(extra_headers); |
| r2->SetRequestHeadersCallback(req_headers_callback); |
| r2->SetResponseHeadersCallback(resp_headers_callback); |
| r2->SetLoadFlags(LOAD_VALIDATE_CACHE); |
| r2->set_isolation_info(isolation_info1_); |
| r2->Start(); |
| delegate.RunUntilComplete(); |
| EXPECT_FALSE(r2->is_pending()); |
| ASSERT_EQ(raw_req_headers.size(), 3u); |
| ASSERT_EQ(raw_resp_headers.size(), 3u); |
| EXPECT_TRUE(raw_req_headers[2]->FindHeaderForTest("If-None-Match", &value)); |
| EXPECT_NE(raw_resp_headers[2].get(), r2->response_headers()); |
| EXPECT_EQ(304, raw_resp_headers[2]->response_code()); |
| EXPECT_EQ("Not Modified", raw_resp_headers[2]->GetStatusText()); |
| } |
| |
| TEST_F(URLRequestTest, UpgradeIfInsecureFlagSet) { |
| TestDelegate d; |
| const GURL kOriginalUrl("https://original.test"); |
| const GURL kRedirectUrl("http://redirect.test"); |
| auto context_builder = CreateTestURLRequestContextBuilder(); |
| auto& network_delegate = *context_builder->set_network_delegate( |
| std::make_unique<BlockingNetworkDelegate>( |
| BlockingNetworkDelegate::SYNCHRONOUS)); |
| network_delegate.set_redirect_url(kRedirectUrl); |
| auto context = context_builder->Build(); |
| |
| std::unique_ptr<URLRequest> r(context->CreateRequest( |
| kOriginalUrl, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| r->set_upgrade_if_insecure(true); |
| r->Start(); |
| d.RunUntilRedirect(); |
| GURL::Replacements replacements; |
| // Check that the redirect URL was upgraded to HTTPS since upgrade_if_insecure |
| // was set. |
| replacements.SetSchemeStr("https"); |
| EXPECT_EQ(kRedirectUrl.ReplaceComponents(replacements), |
| d.redirect_info().new_url); |
| EXPECT_TRUE(d.redirect_info().insecure_scheme_was_upgraded); |
| } |
| |
| TEST_F(URLRequestTest, UpgradeIfInsecureFlagSetExplicitPort80) { |
| TestDelegate d; |
| const GURL kOriginalUrl("https://original.test"); |
| const GURL kRedirectUrl("http://redirect.test:80"); |
| auto context_builder = CreateTestURLRequestContextBuilder(); |
| auto& network_delegate = *context_builder->set_network_delegate( |
| std::make_unique<BlockingNetworkDelegate>( |
| BlockingNetworkDelegate::SYNCHRONOUS)); |
| network_delegate.set_redirect_url(kRedirectUrl); |
| auto context = context_builder->Build(); |
| |
| std::unique_ptr<URLRequest> r(context->CreateRequest( |
| kOriginalUrl, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| r->set_upgrade_if_insecure(true); |
| r->Start(); |
| d.RunUntilRedirect(); |
| GURL::Replacements replacements; |
| // The URL host should have not been changed. |
| EXPECT_EQ(d.redirect_info().new_url.host(), kRedirectUrl.host()); |
| // The scheme should now be https, and the effective port should now be 443. |
| EXPECT_TRUE(d.redirect_info().new_url.SchemeIs("https")); |
| EXPECT_EQ(d.redirect_info().new_url.EffectiveIntPort(), 443); |
| EXPECT_TRUE(d.redirect_info().insecure_scheme_was_upgraded); |
| } |
| |
| TEST_F(URLRequestTest, UpgradeIfInsecureFlagSetNonStandardPort) { |
| TestDelegate d; |
| const GURL kOriginalUrl("https://original.test"); |
| const GURL kRedirectUrl("http://redirect.test:1234"); |
| auto context_builder = CreateTestURLRequestContextBuilder(); |
| auto& network_delegate = *context_builder->set_network_delegate( |
| std::make_unique<BlockingNetworkDelegate>( |
| BlockingNetworkDelegate::SYNCHRONOUS)); |
| network_delegate.set_redirect_url(kRedirectUrl); |
| auto context = context_builder->Build(); |
| |
| std::unique_ptr<URLRequest> r(context->CreateRequest( |
| kOriginalUrl, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| r->set_upgrade_if_insecure(true); |
| r->Start(); |
| d.RunUntilRedirect(); |
| GURL::Replacements replacements; |
| // Check that the redirect URL was upgraded to HTTPS since upgrade_if_insecure |
| // was set, nonstandard port should not have been modified. |
| replacements.SetSchemeStr("https"); |
| EXPECT_EQ(kRedirectUrl.ReplaceComponents(replacements), |
| d.redirect_info().new_url); |
| EXPECT_TRUE(d.redirect_info().insecure_scheme_was_upgraded); |
| } |
| |
| TEST_F(URLRequestTest, UpgradeIfInsecureFlagNotSet) { |
| TestDelegate d; |
| const GURL kOriginalUrl("https://original.test"); |
| const GURL kRedirectUrl("http://redirect.test"); |
| auto context_builder = CreateTestURLRequestContextBuilder(); |
| auto& network_delegate = *context_builder->set_network_delegate( |
| std::make_unique<BlockingNetworkDelegate>( |
| BlockingNetworkDelegate::SYNCHRONOUS)); |
| network_delegate.set_redirect_url(kRedirectUrl); |
| auto context = context_builder->Build(); |
| |
| std::unique_ptr<URLRequest> r(context->CreateRequest( |
| kOriginalUrl, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| r->set_upgrade_if_insecure(false); |
| r->Start(); |
| d.RunUntilRedirect(); |
| // The redirect URL should not be changed if the upgrade_if_insecure flag is |
| // not set. |
| EXPECT_EQ(kRedirectUrl, d.redirect_info().new_url); |
| EXPECT_FALSE(d.redirect_info().insecure_scheme_was_upgraded); |
| } |
| |
| // Test that URLRequests get properly tagged. |
| #if BUILDFLAG(IS_ANDROID) |
| TEST_F(URLRequestTestHTTP, TestTagging) { |
| if (!CanGetTaggedBytes()) { |
| DVLOG(0) << "Skipping test - GetTaggedBytes unsupported."; |
| return; |
| } |
| |
| ASSERT_TRUE(http_test_server()->Start()); |
| |
| // The tag under which the system reports untagged traffic. |
| static const int32_t UNTAGGED_TAG = 0; |
| |
| uint64_t old_traffic = GetTaggedBytes(UNTAGGED_TAG); |
| |
| // Untagged traffic should be tagged with tag UNTAGGED_TAG. |
| TestDelegate delegate; |
| std::unique_ptr<URLRequest> req(default_context().CreateRequest( |
| http_test_server()->GetURL("/"), DEFAULT_PRIORITY, &delegate, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| EXPECT_EQ(SocketTag(), req->socket_tag()); |
| req->Start(); |
| delegate.RunUntilComplete(); |
| |
| EXPECT_GT(GetTaggedBytes(UNTAGGED_TAG), old_traffic); |
| |
| int32_t tag_val1 = 0x12345678; |
| SocketTag tag1(SocketTag::UNSET_UID, tag_val1); |
| old_traffic = GetTaggedBytes(tag_val1); |
| |
| // Test specific tag value. |
| req = default_context().CreateRequest(http_test_server()->GetURL("/"), |
| DEFAULT_PRIORITY, &delegate, |
| TRAFFIC_ANNOTATION_FOR_TESTS); |
| req->set_socket_tag(tag1); |
| EXPECT_EQ(tag1, req->socket_tag()); |
| req->Start(); |
| delegate.RunUntilComplete(); |
| |
| EXPECT_GT(GetTaggedBytes(tag_val1), old_traffic); |
| } |
| #endif |
| |
| namespace { |
| |
| class ReadBufferingListener |
| : public test_server::EmbeddedTestServerConnectionListener { |
| public: |
| ReadBufferingListener() = default; |
| ~ReadBufferingListener() override = default; |
| |
| void BufferNextConnection(int buffer_size) { buffer_size_ = buffer_size; } |
| |
| std::unique_ptr<StreamSocket> AcceptedSocket( |
| std::unique_ptr<StreamSocket> socket) override { |
| if (!buffer_size_) { |
| return socket; |
| } |
| auto wrapped = |
| std::make_unique<ReadBufferingStreamSocket>(std::move(socket)); |
| wrapped->BufferNextRead(buffer_size_); |
| // Do not buffer subsequent connections, which may be a 0-RTT retry. |
| buffer_size_ = 0; |
| return wrapped; |
| } |
| |
| void ReadFromSocket(const StreamSocket& socket, int rv) override {} |
| |
| private: |
| int buffer_size_ = 0; |
| }; |
| |
| // Provides a response to the 0RTT request indicating whether it was received |
| // as early data, sending HTTP_TOO_EARLY if enabled. |
| class ZeroRTTResponse : public test_server::BasicHttpResponse { |
| public: |
| ZeroRTTResponse(bool zero_rtt, bool send_too_early) |
| : zero_rtt_(zero_rtt), send_too_early_(send_too_early) {} |
| |
| ZeroRTTResponse(const ZeroRTTResponse&) = delete; |
| ZeroRTTResponse& operator=(const ZeroRTTResponse&) = delete; |
| |
| ~ZeroRTTResponse() override = default; |
| |
| void SendResponse( |
| base::WeakPtr<test_server::HttpResponseDelegate> delegate) override { |
| AddCustomHeader("Vary", "Early-Data"); |
| set_content_type("text/plain"); |
| AddCustomHeader("Cache-Control", "no-cache"); |
| if (zero_rtt_) { |
| if (send_too_early_) |
| set_code(HTTP_TOO_EARLY); |
| set_content("1"); |
| } else { |
| set_content("0"); |
| } |
| |
| // Since the EmbeddedTestServer doesn't keep the socket open by default, |
| // it is explicitly kept alive to allow the remaining leg of the 0RTT |
| // handshake to be received after the early data. |
| delegate->SendResponseHeaders(code(), GetHttpReasonPhrase(code()), |
| BuildHeaders()); |
| delegate->SendContents(content(), base::DoNothing()); |
| } |
| |
| private: |
| bool zero_rtt_; |
| bool send_too_early_; |
| }; |
| |
| std::unique_ptr<test_server::HttpResponse> HandleZeroRTTRequest( |
| const test_server::HttpRequest& request) { |
| DCHECK(request.ssl_info); |
| |
| if (request.GetURL().path() != "/zerortt") |
| return nullptr; |
| return std::make_unique<ZeroRTTResponse>( |
| request.ssl_info->early_data_received, false); |
| } |
| |
| } // namespace |
| |
| class HTTPSEarlyDataTest : public TestWithTaskEnvironment { |
| public: |
| HTTPSEarlyDataTest() : test_server_(net::EmbeddedTestServer::TYPE_HTTPS) { |
| HttpNetworkSessionParams params; |
| params.enable_early_data = true; |
| |
| auto cert_verifier = std::make_unique<MockCertVerifier>(); |
| cert_verifier->set_default_result(OK); |
| |
| SSLContextConfig config; |
| config.version_max = SSL_PROTOCOL_VERSION_TLS1_3; |
| |
| auto context_builder = CreateTestURLRequestContextBuilder(); |
| context_builder->set_http_network_session_params(params); |
| context_builder->SetCertVerifier(std::move(cert_verifier)); |
| context_builder->set_ssl_config_service( |
| std::make_unique<TestSSLConfigService>(config)); |
| context_ = context_builder->Build(); |
| |
| test_server_.SetSSLConfig( |
| net::EmbeddedTestServer::CERT_OK, |
| CreateSSLServerConfig(SSL_PROTOCOL_VERSION_TLS1_3)); |
| RegisterDefaultHandlers(&test_server_); |
| test_server_.RegisterRequestHandler( |
| base::BindRepeating(&HandleZeroRTTRequest)); |
| test_server_.SetConnectionListener(&listener_); |
| } |
| |
| ~HTTPSEarlyDataTest() override = default; |
| |
| URLRequestContext& context() { return *context_; } |
| |
| static SSLServerConfig CreateSSLServerConfig(uint16_t version) { |
| SSLServerConfig ssl_config; |
| ssl_config.version_max = version; |
| ssl_config.early_data_enabled = true; |
| return ssl_config; |
| } |
| |
| void ResetSSLConfig(net::EmbeddedTestServer::ServerCertificate cert, |
| uint16_t version) { |
| SSLServerConfig ssl_config = CreateSSLServerConfig(version); |
| test_server_.ResetSSLConfig(cert, ssl_config); |
| } |
| |
| protected: |
| std::unique_ptr<URLRequestContext> context_; |
| |
| ReadBufferingListener listener_; |
| EmbeddedTestServer test_server_; |
| }; |
| |
| // TLSEarlyDataTest tests that we handle early data correctly. |
| TEST_F(HTTPSEarlyDataTest, TLSEarlyDataTest) { |
| ASSERT_TRUE(test_server_.Start()); |
| context().http_transaction_factory()->GetSession()->ClearSSLSessionCache(); |
| |
| // kParamSize must be larger than any ClientHello sent by the client, but |
| // smaller than the maximum amount of early data allowed by the server. |
| const int kParamSize = 4 * 1024; |
| const GURL kUrl = |
| test_server_.GetURL("/zerortt?" + std::string(kParamSize, 'a')); |
| |
| { |
| TestDelegate d; |
| std::unique_ptr<URLRequest> r(context().CreateRequest( |
| kUrl, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| r->Start(); |
| EXPECT_TRUE(r->is_pending()); |
| |
| base::RunLoop().Run(); |
| |
| EXPECT_EQ(1, d.response_started_count()); |
| |
| EXPECT_EQ(SSL_CONNECTION_VERSION_TLS1_3, |
| SSLConnectionStatusToVersion(r->ssl_info().connection_status)); |
| EXPECT_TRUE(r->ssl_info().unverified_cert.get()); |
| EXPECT_TRUE(test_server_.GetCertificate()->EqualsIncludingChain( |
| r->ssl_info().cert.get())); |
| |
| // The Early-Data header should be omitted in the initial request, and the |
| // handler should return "0". |
| EXPECT_EQ("0", d.data_received()); |
| } |
| |
| context().http_transaction_factory()->GetSession()->CloseAllConnections( |
| ERR_FAILED, "Very good reason"); |
| |
| // 0-RTT inherently involves a race condition: if the server responds with the |
| // ServerHello before the client sends the HTTP request (the client may be |
| // busy verifying a certificate), the client will send data over 1-RTT keys |
| // rather than 0-RTT. |
| // |
| // This test ensures 0-RTT is sent if relevant by making the test server wait |
| // for both the ClientHello and 0-RTT HTTP request before responding. We use |
| // a ReadBufferingStreamSocket and enable buffering for the 0-RTT request. The |
| // buffer size must be larger than the ClientHello but smaller than the |
| // ClientHello combined with the HTTP request. |
| listener_.BufferNextConnection(kParamSize); |
| |
| { |
| TestDelegate d; |
| std::unique_ptr<URLRequest> r(context().CreateRequest( |
| kUrl, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| |
| r->Start(); |
| EXPECT_TRUE(r->is_pending()); |
| |
| base::RunLoop().Run(); |
| |
| EXPECT_EQ(1, d.response_started_count()); |
| |
| EXPECT_EQ(SSL_CONNECTION_VERSION_TLS1_3, |
| SSLConnectionStatusToVersion(r->ssl_info().connection_status)); |
| EXPECT_TRUE(r->ssl_info().unverified_cert.get()); |
| EXPECT_TRUE(test_server_.GetCertificate()->EqualsIncludingChain( |
| r->ssl_info().cert.get())); |
| |
| // The Early-Data header should be a single '1' in the resumed request, and |
| // the handler should return "1". |
| EXPECT_EQ("1", d.data_received()); |
| } |
| } |
| |
| // TLSEarlyDataTest tests that we handle early data correctly for POST. |
| TEST_F(HTTPSEarlyDataTest, TLSEarlyDataPOSTTest) { |
| ASSERT_TRUE(test_server_.Start()); |
| context().http_transaction_factory()->GetSession()->ClearSSLSessionCache(); |
| |
| { |
| TestDelegate d; |
| std::unique_ptr<URLRequest> r(context().CreateRequest( |
| test_server_.GetURL("/zerortt"), DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| r->Start(); |
| EXPECT_TRUE(r->is_pending()); |
| |
| base::RunLoop().Run(); |
| |
| EXPECT_EQ(1, d.response_started_count()); |
| |
| EXPECT_EQ(SSL_CONNECTION_VERSION_TLS1_3, |
| SSLConnectionStatusToVersion(r->ssl_info().connection_status)); |
| EXPECT_TRUE(r->ssl_info().unverified_cert.get()); |
| EXPECT_TRUE(test_server_.GetCertificate()->EqualsIncludingChain( |
| r->ssl_info().cert.get())); |
| |
| // The Early-Data header should be omitted in the initial request, and the |
| // handler should return "0". |
| EXPECT_EQ("0", d.data_received()); |
| } |
| |
| context().http_transaction_factory()->GetSession()->CloseAllConnections( |
| ERR_FAILED, "Very good reason"); |
| |
| { |
| TestDelegate d; |
| std::unique_ptr<URLRequest> r(context().CreateRequest( |
| test_server_.GetURL("/zerortt"), DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| r->set_method("POST"); |
| r->Start(); |
| EXPECT_TRUE(r->is_pending()); |
| |
| base::RunLoop().Run(); |
| |
| EXPECT_EQ(1, d.response_started_count()); |
| |
| EXPECT_EQ(SSL_CONNECTION_VERSION_TLS1_3, |
| SSLConnectionStatusToVersion(r->ssl_info().connection_status)); |
| EXPECT_TRUE(r->ssl_info().unverified_cert.get()); |
| EXPECT_TRUE(test_server_.GetCertificate()->EqualsIncludingChain( |
| r->ssl_info().cert.get())); |
| |
| // The Early-Data header should be omitted in the request, since we don't |
| // send POSTs over early data, and the handler should return "0". |
| EXPECT_EQ("0", d.data_received()); |
| } |
| } |
| |
| // TLSEarlyDataTest tests that the 0-RTT is enabled for idempotent POST request. |
| TEST_F(HTTPSEarlyDataTest, TLSEarlyDataIdempotentPOSTTest) { |
| ASSERT_TRUE(test_server_.Start()); |
| context().http_transaction_factory()->GetSession()->ClearSSLSessionCache(); |
| const int kParamSize = 4 * 1024; |
| const GURL kUrl = |
| test_server_.GetURL("/zerortt?" + std::string(kParamSize, 'a')); |
| |
| { |
| TestDelegate d; |
| std::unique_ptr<URLRequest> r(context().CreateRequest( |
| kUrl, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| r->Start(); |
| EXPECT_TRUE(r->is_pending()); |
| |
| base::RunLoop().Run(); |
| |
| EXPECT_EQ(1, d.response_started_count()); |
| |
| EXPECT_EQ(SSL_CONNECTION_VERSION_TLS1_3, |
| SSLConnectionStatusToVersion(r->ssl_info().connection_status)); |
| EXPECT_TRUE(r->ssl_info().unverified_cert.get()); |
| EXPECT_TRUE(test_server_.GetCertificate()->EqualsIncludingChain( |
| r->ssl_info().cert.get())); |
| |
| // The Early-Data header should be omitted in the initial request, and the |
| // handler should return "0". |
| EXPECT_EQ("0", d.data_received()); |
| } |
| |
| context().http_transaction_factory()->GetSession()->CloseAllConnections( |
| ERR_FAILED, "Very good reason"); |
| listener_.BufferNextConnection(kParamSize); |
| |
| { |
| TestDelegate d; |
| std::unique_ptr<URLRequest> r(context().CreateRequest( |
| kUrl, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| r->set_method("POST"); |
| r->SetIdempotency(net::IDEMPOTENT); |
| r->Start(); |
| EXPECT_TRUE(r->is_pending()); |
| |
| base::RunLoop().Run(); |
| |
| EXPECT_EQ(1, d.response_started_count()); |
| |
| EXPECT_EQ(SSL_CONNECTION_VERSION_TLS1_3, |
| SSLConnectionStatusToVersion(r->ssl_info().connection_status)); |
| EXPECT_TRUE(r->ssl_info().unverified_cert.get()); |
| EXPECT_TRUE(test_server_.GetCertificate()->EqualsIncludingChain( |
| r->ssl_info().cert.get())); |
| |
| // The Early-Data header should be set since the request is set as an |
| // idempotent POST request. |
| EXPECT_EQ("1", d.data_received()); |
| } |
| } |
| |
| // TLSEarlyDataTest tests that the 0-RTT is disabled for non-idempotent request. |
| TEST_F(HTTPSEarlyDataTest, TLSEarlyDataNonIdempotentRequestTest) { |
| ASSERT_TRUE(test_server_.Start()); |
| context().http_transaction_factory()->GetSession()->ClearSSLSessionCache(); |
| |
| { |
| TestDelegate d; |
| std::unique_ptr<URLRequest> r(context().CreateRequest( |
| test_server_.GetURL("/zerortt"), DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| r->Start(); |
| EXPECT_TRUE(r->is_pending()); |
| |
| base::RunLoop().Run(); |
| |
| EXPECT_EQ(1, d.response_started_count()); |
| |
| EXPECT_EQ(SSL_CONNECTION_VERSION_TLS1_3, |
| SSLConnectionStatusToVersion(r->ssl_info().connection_status)); |
| EXPECT_TRUE(r->ssl_info().unverified_cert.get()); |
| EXPECT_TRUE(test_server_.GetCertificate()->EqualsIncludingChain( |
| r->ssl_info().cert.get())); |
| |
| // The Early-Data header should be omitted in the initial request, and the |
| // handler should return "0". |
| EXPECT_EQ("0", d.data_received()); |
| } |
| |
| context().http_transaction_factory()->GetSession()->CloseAllConnections( |
| ERR_FAILED, "Very good reason"); |
| |
| { |
| TestDelegate d; |
| std::unique_ptr<URLRequest> r(context().CreateRequest( |
| test_server_.GetURL("/zerortt"), DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| // Sets the GET request as not idempotent. |
| r->SetIdempotency(net::NOT_IDEMPOTENT); |
| r->Start(); |
| EXPECT_TRUE(r->is_pending()); |
| |
| base::RunLoop().Run(); |
| |
| EXPECT_EQ(1, d.response_started_count()); |
| |
| EXPECT_EQ(SSL_CONNECTION_VERSION_TLS1_3, |
| SSLConnectionStatusToVersion(r->ssl_info().connection_status)); |
| EXPECT_TRUE(r->ssl_info().unverified_cert.get()); |
| EXPECT_TRUE(test_server_.GetCertificate()->EqualsIncludingChain( |
| r->ssl_info().cert.get())); |
| |
| // The Early-Data header should be omitted in the initial request even |
| // though it is a GET request, since the request is set as not idempotent. |
| EXPECT_EQ("0", d.data_received()); |
| } |
| } |
| |
| std::unique_ptr<test_server::HttpResponse> HandleTooEarly( |
| bool* sent_425, |
| const test_server::HttpRequest& request) { |
| DCHECK(request.ssl_info); |
| |
| if (request.GetURL().path() != "/tooearly") |
| return nullptr; |
| if (request.ssl_info->early_data_received) |
| *sent_425 = true; |
| return std::make_unique<ZeroRTTResponse>( |
| request.ssl_info->early_data_received, true); |
| } |
| |
| // Test that we handle 425 (Too Early) correctly. |
| TEST_F(HTTPSEarlyDataTest, TLSEarlyDataTooEarlyTest) { |
| bool sent_425 = false; |
| test_server_.RegisterRequestHandler( |
| base::BindRepeating(&HandleTooEarly, base::Unretained(&sent_425))); |
| ASSERT_TRUE(test_server_.Start()); |
| context().http_transaction_factory()->GetSession()->ClearSSLSessionCache(); |
| |
| // kParamSize must be larger than any ClientHello sent by the client, but |
| // smaller than the maximum amount of early data allowed by the server. |
| const int kParamSize = 4 * 1024; |
| const GURL kUrl = |
| test_server_.GetURL("/tooearly?" + std::string(kParamSize, 'a')); |
| |
| { |
| TestDelegate d; |
| std::unique_ptr<URLRequest> r(context().CreateRequest( |
| kUrl, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| r->Start(); |
| EXPECT_TRUE(r->is_pending()); |
| |
| d.RunUntilComplete(); |
| |
| EXPECT_EQ(1, d.response_started_count()); |
| |
| EXPECT_EQ(SSL_CONNECTION_VERSION_TLS1_3, |
| SSLConnectionStatusToVersion(r->ssl_info().connection_status)); |
| EXPECT_TRUE(r->ssl_info().unverified_cert.get()); |
| EXPECT_TRUE(test_server_.GetCertificate()->EqualsIncludingChain( |
| r->ssl_info().cert.get())); |
| |
| // The Early-Data header should be omitted in the initial request, and the |
| // handler should return "0". |
| EXPECT_EQ("0", d.data_received()); |
| EXPECT_FALSE(sent_425); |
| } |
| |
| context().http_transaction_factory()->GetSession()->CloseAllConnections( |
| ERR_FAILED, "Very good reason"); |
| |
| // 0-RTT inherently involves a race condition: if the server responds with the |
| // ServerHello before the client sends the HTTP request (the client may be |
| // busy verifying a certificate), the client will send data over 1-RTT keys |
| // rather than 0-RTT. |
| // |
| // This test ensures 0-RTT is sent if relevant by making the test server wait |
| // for both the ClientHello and 0-RTT HTTP request before responding. We use |
| // a ReadBufferingStreamSocket and enable buffering for the 0-RTT request. The |
| // buffer size must be larger than the ClientHello but smaller than the |
| // ClientHello combined with the HTTP request. |
| // |
| // We must buffer exactly one connection because the HTTP 425 response will |
| // trigger a retry, potentially on a new connection. |
| listener_.BufferNextConnection(kParamSize); |
| |
| { |
| TestDelegate d; |
| std::unique_ptr<URLRequest> r(context().CreateRequest( |
| kUrl, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| |
| r->Start(); |
| EXPECT_TRUE(r->is_pending()); |
| |
| d.RunUntilComplete(); |
| |
| EXPECT_EQ(1, d.response_started_count()); |
| |
| EXPECT_EQ(SSL_CONNECTION_VERSION_TLS1_3, |
| SSLConnectionStatusToVersion(r->ssl_info().connection_status)); |
| EXPECT_TRUE(r->ssl_info().unverified_cert.get()); |
| EXPECT_TRUE(test_server_.GetCertificate()->EqualsIncludingChain( |
| r->ssl_info().cert.get())); |
| |
| // The resumption request will encounter a 425 error and retry without early |
| // data, and the handler should return "0". |
| EXPECT_EQ("0", d.data_received()); |
| EXPECT_TRUE(sent_425); |
| } |
| } |
| |
| // TLSEarlyDataRejectTest tests that we gracefully handle an early data reject |
| // and retry without early data. |
| TEST_F(HTTPSEarlyDataTest, TLSEarlyDataRejectTest) { |
| ASSERT_TRUE(test_server_.Start()); |
| context().http_transaction_factory()->GetSession()->ClearSSLSessionCache(); |
| |
| { |
| TestDelegate d; |
| std::unique_ptr<URLRequest> r(context().CreateRequest( |
| test_server_.GetURL("/zerortt"), DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| |
| r->Start(); |
| EXPECT_TRUE(r->is_pending()); |
| |
| d.RunUntilComplete(); |
| |
| EXPECT_EQ(SSL_CONNECTION_VERSION_TLS1_3, |
| SSLConnectionStatusToVersion(r->ssl_info().connection_status)); |
| EXPECT_TRUE(r->ssl_info().unverified_cert.get()); |
| EXPECT_TRUE(test_server_.GetCertificate()->EqualsIncludingChain( |
| r->ssl_info().cert.get())); |
| |
| // The Early-Data header should be omitted in the initial request, and the |
| // handler should return "0". |
| EXPECT_EQ("0", d.data_received()); |
| } |
| |
| context().http_transaction_factory()->GetSession()->CloseAllConnections( |
| ERR_FAILED, "Very good reason"); |
| |
| // The certificate in the resumption is changed to confirm that the |
| // certificate change is observed. |
| scoped_refptr<X509Certificate> old_cert = test_server_.GetCertificate(); |
| ResetSSLConfig(net::EmbeddedTestServer::CERT_EXPIRED, |
| SSL_PROTOCOL_VERSION_TLS1_3); |
| |
| { |
| TestDelegate d; |
| std::unique_ptr<URLRequest> r(context().CreateRequest( |
| test_server_.GetURL("/zerortt"), DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| |
| r->Start(); |
| EXPECT_TRUE(r->is_pending()); |
| |
| d.RunUntilComplete(); |
| |
| EXPECT_EQ(1, d.response_started_count()); |
| |
| EXPECT_EQ(SSL_CONNECTION_VERSION_TLS1_3, |
| SSLConnectionStatusToVersion(r->ssl_info().connection_status)); |
| EXPECT_TRUE(r->ssl_info().unverified_cert.get()); |
| EXPECT_TRUE(test_server_.GetCertificate()->EqualsIncludingChain( |
| r->ssl_info().cert.get())); |
| EXPECT_FALSE(old_cert->EqualsIncludingChain(r->ssl_info().cert.get())); |
| |
| // The Early-Data header should be omitted in the rejected request, and the |
| // handler should return "0". |
| EXPECT_EQ("0", d.data_received()); |
| } |
| } |
| |
| // TLSEarlyDataTLS12RejectTest tests that we gracefully handle an early data |
| // reject from a TLS 1.2 server and retry without early data. |
| TEST_F(HTTPSEarlyDataTest, TLSEarlyDataTLS12RejectTest) { |
| ASSERT_TRUE(test_server_.Start()); |
| context().http_transaction_factory()->GetSession()->ClearSSLSessionCache(); |
| |
| { |
| TestDelegate d; |
| std::unique_ptr<URLRequest> r(context().CreateRequest( |
| test_server_.GetURL("/zerortt"), DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| |
| r->Start(); |
| EXPECT_TRUE(r->is_pending()); |
| |
| d.RunUntilComplete(); |
| |
| EXPECT_EQ(1, d.response_started_count()); |
| |
| EXPECT_EQ(SSL_CONNECTION_VERSION_TLS1_3, |
| SSLConnectionStatusToVersion(r->ssl_info().connection_status)); |
| EXPECT_TRUE(r->ssl_info().unverified_cert.get()); |
| EXPECT_TRUE(test_server_.GetCertificate()->EqualsIncludingChain( |
| r->ssl_info().cert.get())); |
| |
| // The Early-Data header should be omitted in the initial request, and the |
| // handler should return "0". |
| EXPECT_EQ("0", d.data_received()); |
| } |
| |
| context().http_transaction_factory()->GetSession()->CloseAllConnections( |
| ERR_FAILED, "Very good reason"); |
| |
| // The certificate in the resumption is changed to confirm that the |
| // certificate change is observed. |
| scoped_refptr<X509Certificate> old_cert = test_server_.GetCertificate(); |
| ResetSSLConfig(net::EmbeddedTestServer::CERT_EXPIRED, |
| SSL_PROTOCOL_VERSION_TLS1_2); |
| |
| { |
| TestDelegate d; |
| std::unique_ptr<URLRequest> r(context().CreateRequest( |
| test_server_.GetURL("/zerortt"), DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| |
| r->Start(); |
| EXPECT_TRUE(r->is_pending()); |
| |
| d.RunUntilComplete(); |
| |
| EXPECT_EQ(1, d.response_started_count()); |
| |
| EXPECT_EQ(SSL_CONNECTION_VERSION_TLS1_2, |
| SSLConnectionStatusToVersion(r->ssl_info().connection_status)); |
| EXPECT_TRUE(r->ssl_info().unverified_cert.get()); |
| EXPECT_TRUE(test_server_.GetCertificate()->EqualsIncludingChain( |
| r->ssl_info().cert.get())); |
| EXPECT_FALSE(old_cert->EqualsIncludingChain(r->ssl_info().cert.get())); |
| |
| // The Early-Data header should be omitted in the rejected request, and the |
| // handler should return "0". |
| EXPECT_EQ("0", d.data_received()); |
| } |
| } |
| |
| // Tests that AuthChallengeInfo is available on the request. |
| TEST_F(URLRequestTestHTTP, AuthChallengeInfo) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| GURL url(http_test_server()->GetURL("/auth-basic")); |
| |
| TestDelegate delegate; |
| |
| std::unique_ptr<URLRequest> r(default_context().CreateRequest( |
| url, DEFAULT_PRIORITY, &delegate, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| r->Start(); |
| delegate.RunUntilComplete(); |
| ASSERT_TRUE(r->auth_challenge_info().has_value()); |
| EXPECT_FALSE(r->auth_challenge_info()->is_proxy); |
| EXPECT_EQ(url::SchemeHostPort(url), r->auth_challenge_info()->challenger); |
| EXPECT_EQ("basic", r->auth_challenge_info()->scheme); |
| EXPECT_EQ("testrealm", r->auth_challenge_info()->realm); |
| EXPECT_EQ("Basic realm=\"testrealm\"", r->auth_challenge_info()->challenge); |
| EXPECT_EQ("/auth-basic", r->auth_challenge_info()->path); |
| } |
| |
| TEST_F(URLRequestTestHTTP, ConnectNoSupported) { |
| ASSERT_TRUE(http_test_server()->Start()); |
| TestDelegate delegate; |
| std::unique_ptr<URLRequest> r(default_context().CreateRequest( |
| http_test_server()->GetURL("/"), DEFAULT_PRIORITY, &delegate, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| r->set_method("CONNECT"); |
| r->Start(); |
| delegate.RunUntilComplete(); |
| EXPECT_EQ(ERR_METHOD_NOT_SUPPORTED, delegate.request_status()); |
| } |
| |
| class URLRequestDnsAliasTest : public TestWithTaskEnvironment { |
| protected: |
| URLRequestDnsAliasTest() { |
| auto context_builder = CreateTestURLRequestContextBuilder(); |
| context_builder->set_host_resolver(std::make_unique<MockHostResolver>()); |
| context_ = context_builder->Build(); |
| } |
| |
| URLRequestContext& context() { return *context_; } |
| MockHostResolver& host_resolver() { |
| // This cast is safe because we provided a MockHostResolver in the ctor. |
| return *static_cast<MockHostResolver*>(context_->host_resolver()); |
| } |
| |
| void SetUp() override { ASSERT_TRUE(test_server_.Start()); } |
| |
| std::unique_ptr<URLRequestContext> context_; |
| TestDelegate test_delegate_; |
| EmbeddedTestServer test_server_; |
| }; |
| |
| TEST_F(URLRequestDnsAliasTest, WithDnsAliases) { |
| GURL url(test_server_.GetURL("www.example.test", "/echo")); |
| std::vector<std::string> aliases({"alias1", "alias2", "host"}); |
| host_resolver().rules()->AddIPLiteralRuleWithDnsAliases( |
| "www.example.test", "127.0.0.1", std::move(aliases)); |
| |
| std::unique_ptr<URLRequest> request(context().CreateRequest( |
| url, DEFAULT_PRIORITY, &test_delegate_, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| |
| request->Start(); |
| |
| test_delegate_.RunUntilComplete(); |
| EXPECT_THAT(test_delegate_.request_status(), IsOk()); |
| EXPECT_THAT(request->response_info().dns_aliases, |
| testing::ElementsAre("alias1", "alias2", "host")); |
| } |
| |
| TEST_F(URLRequestDnsAliasTest, NoAdditionalDnsAliases) { |
| GURL url(test_server_.GetURL("www.example.test", "/echo")); |
| host_resolver().rules()->AddIPLiteralRuleWithDnsAliases( |
| "www.example.test", "127.0.0.1", /*dns_aliases=*/std::set<std::string>()); |
| |
| std::unique_ptr<URLRequest> request(context().CreateRequest( |
| url, DEFAULT_PRIORITY, &test_delegate_, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| |
| request->Start(); |
| |
| test_delegate_.RunUntilComplete(); |
| EXPECT_THAT(test_delegate_.request_status(), IsOk()); |
| EXPECT_THAT(request->response_info().dns_aliases, |
| testing::ElementsAre("www.example.test")); |
| } |
| |
| TEST_F(URLRequestTest, SetURLChain) { |
| TestDelegate d; |
| { |
| GURL original_url("http://localhost"); |
| std::unique_ptr<URLRequest> r(default_context().CreateRequest( |
| original_url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| EXPECT_EQ(r->url_chain().size(), 1u); |
| EXPECT_EQ(r->url_chain()[0], original_url); |
| |
| const std::vector<GURL> url_chain = { |
| GURL("http://foo.test"), |
| GURL("http://bar.test"), |
| GURL("http://baz.test"), |
| }; |
| |
| r->SetURLChain(url_chain); |
| |
| EXPECT_EQ(r->url_chain().size(), 3u); |
| EXPECT_EQ(r->url_chain()[0], url_chain[0]); |
| EXPECT_EQ(r->url_chain()[1], url_chain[1]); |
| EXPECT_EQ(r->url_chain()[2], original_url); |
| } |
| } |
| |
| TEST_F(URLRequestTest, SetIsolationInfoFromNak) { |
| TestDelegate d; |
| SchemefulSite site_a = SchemefulSite(GURL("https://a.com/")); |
| SchemefulSite site_b = SchemefulSite(GURL("https://b.com/")); |
| base::UnguessableToken nak_nonce = base::UnguessableToken::Create(); |
| auto populated_cross_site_nak = NetworkAnonymizationKey::CreateFromParts( |
| site_a, /*is_cross_site=*/true, nak_nonce); |
| IsolationInfo expected_isolation_info_populated_cross_site_nak = |
| IsolationInfo::Create(IsolationInfo::RequestType::kOther, |
| url::Origin::Create(GURL("https://a.com/")), |
| url::Origin(), SiteForCookies(), |
| /*party_context=*/absl::nullopt, nak_nonce); |
| |
| auto populated_same_site_nak = NetworkAnonymizationKey::CreateFromParts( |
| site_a, /*is_cross_site=*/false, nak_nonce); |
| IsolationInfo expected_isolation_info_populated_same_site_nak = |
| IsolationInfo::Create(IsolationInfo::RequestType::kOther, |
| url::Origin::Create(GURL("https://a.com/")), |
| url::Origin::Create(GURL("https://a.com/")), |
| SiteForCookies(), |
| /*party_context=*/absl::nullopt, nak_nonce); |
| |
| NetworkAnonymizationKey empty_nak; |
| GURL original_url("http://localhost"); |
| std::unique_ptr<URLRequest> r(default_context().CreateRequest( |
| original_url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| |
| r->set_isolation_info_from_network_anonymization_key( |
| populated_cross_site_nak); |
| r->SetLoadFlags(LOAD_DISABLE_CACHE); |
| r->set_allow_credentials(false); |
| EXPECT_TRUE(r->is_created_from_network_anonymization_key()); |
| EXPECT_EQ(r->isolation_info().network_anonymization_key(), |
| populated_cross_site_nak); |
| EXPECT_EQ( |
| r->isolation_info().top_frame_origin(), |
| expected_isolation_info_populated_cross_site_nak.top_frame_origin()); |
| // The cross-site bit in the NAK causes the IsolationInfo's NIK to have, an |
| // anonymous origin, but the bit is not enough to reconstruct a different |
| // frame_site. |
| EXPECT_FALSE(r->isolation_info().IsEqualForTesting( |
| expected_isolation_info_populated_cross_site_nak)); |
| |
| r->set_isolation_info_from_network_anonymization_key(populated_same_site_nak); |
| EXPECT_TRUE(r->is_created_from_network_anonymization_key()); |
| EXPECT_EQ(r->isolation_info().network_anonymization_key(), |
| populated_same_site_nak); |
| EXPECT_TRUE(r->isolation_info().IsEqualForTesting( |
| expected_isolation_info_populated_same_site_nak)); |
| |
| r->set_isolation_info_from_network_anonymization_key(empty_nak); |
| EXPECT_TRUE(r->is_created_from_network_anonymization_key()); |
| EXPECT_EQ(r->isolation_info().network_anonymization_key(), empty_nak); |
| EXPECT_TRUE(r->isolation_info().IsEqualForTesting(net::IsolationInfo())); |
| r->Start(); |
| d.RunUntilComplete(); |
| } |
| |
| TEST_F(URLRequestTest, CookiePartitionKey) { |
| const url::Origin kOrigin = url::Origin::Create(GURL("http://foo.test/")); |
| |
| { // Partitioned cookies disabled. |
| base::test::ScopedFeatureList feature_list; |
| feature_list.InitAndDisableFeature(features::kPartitionedCookies); |
| TestDelegate d; |
| std::unique_ptr<URLRequest> r(default_context().CreateRequest( |
| GURL("ws://foo.test/"), DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| r->set_isolation_info( |
| IsolationInfo::Create(IsolationInfo::RequestType::kMainFrame, kOrigin, |
| kOrigin, SiteForCookies::FromOrigin(kOrigin))); |
| EXPECT_FALSE(r->cookie_partition_key()); |
| } |
| |
| { // Partitioned cookies enabled. |
| base::test::ScopedFeatureList feature_list; |
| feature_list.InitAndEnableFeature(features::kPartitionedCookies); |
| TestDelegate d; |
| std::unique_ptr<URLRequest> r(default_context().CreateRequest( |
| GURL("ws://foo.test/"), DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| r->set_isolation_info( |
| IsolationInfo::Create(IsolationInfo::RequestType::kMainFrame, kOrigin, |
| kOrigin, SiteForCookies::FromOrigin(kOrigin))); |
| EXPECT_TRUE(r->cookie_partition_key()); |
| EXPECT_EQ(r->cookie_partition_key()->site(), SchemefulSite(kOrigin)); |
| } |
| } |
| |
| class URLRequestMaybeAsyncFirstPartySetsTest |
| : public URLRequestTest, |
| public testing::WithParamInterface<bool> { |
| public: |
| URLRequestMaybeAsyncFirstPartySetsTest() { CHECK(test_server_.Start()); } |
| |
| std::unique_ptr<CookieStore> CreateCookieStore() { |
| auto cookie_monster = std::make_unique<CookieMonster>(/*store=*/nullptr, |
| /*net_log=*/nullptr); |
| auto cookie_access_delegate = std::make_unique<TestCookieAccessDelegate>(); |
| cookie_access_delegate->set_invoke_callbacks_asynchronously( |
| invoke_callbacks_asynchronously()); |
| cookie_monster->SetCookieAccessDelegate(std::move(cookie_access_delegate)); |
| return cookie_monster; |
| } |
| |
| bool invoke_callbacks_asynchronously() { return GetParam(); } |
| |
| HttpTestServer& test_server() { return test_server_; } |
| |
| private: |
| HttpTestServer test_server_; |
| }; |
| |
| TEST_P(URLRequestMaybeAsyncFirstPartySetsTest, SimpleRequest) { |
| const std::string kHost = "example.test"; |
| const url::Origin kOrigin = |
| url::Origin::Create(test_server().GetURL(kHost, "/")); |
| const SiteForCookies kSiteForCookies = SiteForCookies::FromOrigin(kOrigin); |
| |
| auto context_builder = CreateTestURLRequestContextBuilder(); |
| context_builder->SetCookieStore(CreateCookieStore()); |
| auto context = context_builder->Build(); |
| |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req(context->CreateRequest( |
| test_server().GetURL(kHost, "/echo"), DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->set_isolation_info( |
| IsolationInfo::Create(IsolationInfo::RequestType::kMainFrame, kOrigin, |
| kOrigin, kSiteForCookies, {} /* party_context */)); |
| req->Start(); |
| d.RunUntilComplete(); |
| |
| EXPECT_EQ(d.data_received(), "Echo"); |
| EXPECT_THAT(d.request_status(), IsOk()); |
| EXPECT_EQ(req->GetResponseCode(), 200); |
| } |
| |
| TEST_P(URLRequestMaybeAsyncFirstPartySetsTest, SingleRedirect) { |
| const std::string kHost = "example.test"; |
| const url::Origin kOrigin = |
| url::Origin::Create(test_server().GetURL(kHost, "/")); |
| const SiteForCookies kSiteForCookies = SiteForCookies::FromOrigin(kOrigin); |
| |
| auto context_builder = CreateTestURLRequestContextBuilder(); |
| context_builder->SetCookieStore(CreateCookieStore()); |
| auto context = context_builder->Build(); |
| |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req(context->CreateRequest( |
| test_server().GetURL(kHost, |
| base::StrCat({ |
| "/server-redirect?", |
| test_server().GetURL(kHost, "/echo").spec(), |
| })), |
| DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->set_isolation_info( |
| IsolationInfo::Create(IsolationInfo::RequestType::kMainFrame, kOrigin, |
| kOrigin, kSiteForCookies, {} /* party_context */)); |
| req->Start(); |
| d.RunUntilComplete(); |
| |
| EXPECT_EQ(d.data_received(), "Echo"); |
| EXPECT_THAT(d.request_status(), IsOk()); |
| EXPECT_EQ(req->GetResponseCode(), 200); |
| } |
| |
| INSTANTIATE_TEST_SUITE_P(, |
| URLRequestMaybeAsyncFirstPartySetsTest, |
| testing::Bool()); |
| |
| class PartitionConnectionsByNetworkAnonymizationKey : public URLRequestTest { |
| public: |
| PartitionConnectionsByNetworkAnonymizationKey() { |
| scoped_feature_list_.InitWithFeatures( |
| {net::features::kPartitionConnectionsByNetworkIsolationKey, |
| net::features::kPartitionSSLSessionsByNetworkIsolationKey}, |
| {}); |
| } |
| const SchemefulSite kTestSiteA = SchemefulSite(GURL("http://a.test/")); |
| const SchemefulSite kTestSiteB = SchemefulSite(GURL("http://b.test/")); |
| const SchemefulSite kTestSiteC = SchemefulSite(GURL("http://c.test/")); |
| const base::UnguessableToken kNonceA = base::UnguessableToken::Create(); |
| const base::UnguessableToken kNonceB = base::UnguessableToken::Create(); |
| |
| private: |
| base::test::ScopedFeatureList scoped_feature_list_; |
| }; |
| |
| TEST_F(PartitionConnectionsByNetworkAnonymizationKey, |
| DifferentTopFrameSitesNeverShareConnections) { |
| // Start server |
| EmbeddedTestServer test_server(EmbeddedTestServer::TYPE_HTTPS); |
| RegisterDefaultHandlers(&test_server); |
| ASSERT_TRUE(test_server.Start()); |
| const auto original_url = test_server.GetURL("/echo"); |
| const auto network_anonymization_key1 = |
| NetworkAnonymizationKey::CreateSameSite(kTestSiteA); |
| const auto network_anonymization_key2 = |
| NetworkAnonymizationKey::CreateSameSite(kTestSiteB); |
| |
| // Create a request from first party `kTestSiteA`. |
| { |
| TestDelegate d; |
| std::unique_ptr<URLRequest> r1(default_context().CreateRequest( |
| original_url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| r1->set_isolation_info_from_network_anonymization_key( |
| network_anonymization_key1); |
| r1->SetLoadFlags(LOAD_DISABLE_CACHE); |
| r1->set_allow_credentials(false); |
| |
| // Verify NetworkAnonymizationKey is set correctly |
| EXPECT_TRUE(r1->is_created_from_network_anonymization_key()); |
| EXPECT_EQ(r1->isolation_info().network_anonymization_key(), |
| network_anonymization_key1); |
| // Run request |
| r1->Start(); |
| d.RunUntilComplete(); |
| |
| // Verify request started with a full handshake |
| EXPECT_THAT(d.request_status(), IsOk()); |
| EXPECT_EQ(SSLInfo::HANDSHAKE_FULL, r1->ssl_info().handshake_type); |
| } |
| |
| // Create a request from first party `kTestSiteB`. This request should never |
| // share a key with r1 regardless of the NIK/NAK key schemes. |
| { |
| TestDelegate d; |
| // Create request and create IsolationInfo from |
| // `network_anonymization_key2` |
| std::unique_ptr<URLRequest> r2(default_context().CreateRequest( |
| original_url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| r2->set_isolation_info_from_network_anonymization_key( |
| network_anonymization_key2); |
| r2->SetLoadFlags(LOAD_DISABLE_CACHE); |
| r2->set_allow_credentials(false); |
| |
| // Verify NetworkAnonymizationKey is set correctly. |
| EXPECT_TRUE(r2->is_created_from_network_anonymization_key()); |
| EXPECT_EQ(r2->isolation_info().network_anonymization_key(), |
| network_anonymization_key2); |
| // Run request |
| r2->Start(); |
| d.RunUntilComplete(); |
| |
| // Verify request started with a full handshake |
| EXPECT_EQ(1, d.response_started_count()); |
| EXPECT_EQ(SSLInfo::HANDSHAKE_FULL, r2->ssl_info().handshake_type); |
| } |
| } |
| |
| TEST_F(PartitionConnectionsByNetworkAnonymizationKey, |
| FirstPartyIsSeparatedFromCrossSiteFrames) { |
| // Start server |
| EmbeddedTestServer test_server(EmbeddedTestServer::TYPE_HTTPS); |
| RegisterDefaultHandlers(&test_server); |
| ASSERT_TRUE(test_server.Start()); |
| const auto original_url = test_server.GetURL("/echo"); |
| const auto network_anonymization_key1 = |
| NetworkAnonymizationKey::CreateSameSite(kTestSiteA); |
| const auto network_anonymization_key2 = |
| NetworkAnonymizationKey::CreateCrossSite(kTestSiteA); |
| |
| // Create a request from first party `kTestSiteA`. |
| { |
| TestDelegate d; |
| std::unique_ptr<URLRequest> r1(default_context().CreateRequest( |
| original_url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| r1->set_isolation_info_from_network_anonymization_key( |
| network_anonymization_key1); |
| r1->SetLoadFlags(LOAD_DISABLE_CACHE); |
| r1->set_allow_credentials(false); |
| |
| // Verify NetworkAnonymizationKey is set correctly |
| EXPECT_TRUE(r1->is_created_from_network_anonymization_key()); |
| EXPECT_EQ(r1->isolation_info().network_anonymization_key(), |
| network_anonymization_key1); |
| // Run request |
| r1->Start(); |
| d.RunUntilComplete(); |
| // Verify request started with a full handshake |
| EXPECT_THAT(d.request_status(), IsOk()); |
| EXPECT_EQ(SSLInfo::HANDSHAKE_FULL, r1->ssl_info().handshake_type); |
| } |
| |
| // Create a request from third party `kTestSiteB` embedded in `kTestSiteA`. |
| // This request should share a key with r1 when NetworkAnonymizationKey is in |
| // double keyed scheme and should not share a key with r1 when |
| // NetworkAnonymizationKey is triple keyed or in cross site flag scheme. |
| { |
| TestDelegate d; |
| std::unique_ptr<URLRequest> r2(default_context().CreateRequest( |
| original_url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| r2->set_isolation_info_from_network_anonymization_key( |
| network_anonymization_key2); |
| r2->SetLoadFlags(LOAD_DISABLE_CACHE); |
| r2->set_allow_credentials(false); |
| |
| // Verify NetworkAnonymizationKey is set correctly. |
| EXPECT_TRUE(r2->is_created_from_network_anonymization_key()); |
| EXPECT_EQ(r2->isolation_info().network_anonymization_key(), |
| network_anonymization_key2); |
| // Run request |
| r2->Start(); |
| d.RunUntilComplete(); |
| |
| EXPECT_THAT(d.request_status(), IsOk()); |
| // We should not share a connection with r1. |
| EXPECT_EQ(SSLInfo::HANDSHAKE_FULL, r2->ssl_info().handshake_type); |
| } |
| } |
| |
| TEST_F( |
| PartitionConnectionsByNetworkAnonymizationKey, |
| DifferentCrossSiteFramesAreSeparatedOnlyWhenNetworkAnonymizationKeyIsTripleKeyed) { |
| // Start server |
| EmbeddedTestServer test_server(EmbeddedTestServer::TYPE_HTTPS); |
| RegisterDefaultHandlers(&test_server); |
| ASSERT_TRUE(test_server.Start()); |
| const auto original_url = test_server.GetURL("/echo"); |
| const auto network_anonymization_key1 = |
| NetworkAnonymizationKey::CreateCrossSite(kTestSiteA); |
| const auto network_anonymization_key2 = |
| NetworkAnonymizationKey::CreateCrossSite(kTestSiteA); |
| |
| // Create a request from first party `kTestSiteA`. |
| { |
| TestDelegate d; |
| std::unique_ptr<URLRequest> r1(default_context().CreateRequest( |
| original_url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| r1->set_isolation_info_from_network_anonymization_key( |
| network_anonymization_key1); |
| r1->SetLoadFlags(LOAD_DISABLE_CACHE); |
| r1->set_allow_credentials(false); |
| |
| // Verify NetworkAnonymizationKey is set correctly |
| EXPECT_TRUE(r1->is_created_from_network_anonymization_key()); |
| EXPECT_EQ(r1->isolation_info().network_anonymization_key(), |
| network_anonymization_key1); |
| // Run request |
| r1->Start(); |
| d.RunUntilComplete(); |
| // Verify request started with a full handshake |
| EXPECT_THAT(d.request_status(), IsOk()); |
| EXPECT_EQ(SSLInfo::HANDSHAKE_FULL, r1->ssl_info().handshake_type); |
| } |
| |
| // Create a request from third party `kTestSiteB` embedded in `kTestSiteA`. |
| // This request should share a key with r1 when NetworkAnonymizationKey is in |
| // double keyed scheme and should not share a key with r1 when |
| // NetworkAnonymizationKey is triple keyed or in cross site flag scheme. |
| { |
| TestDelegate d; |
| // Create request and create IsolationInfo from |
| // `network_anonymization_key2` |
| std::unique_ptr<URLRequest> r2(default_context().CreateRequest( |
| original_url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| r2->set_isolation_info_from_network_anonymization_key( |
| network_anonymization_key2); |
| r2->SetLoadFlags(LOAD_DISABLE_CACHE); |
| r2->set_allow_credentials(false); |
| |
| // Verify NetworkAnonymizationKey is set correctly. |
| EXPECT_TRUE(r2->is_created_from_network_anonymization_key()); |
| EXPECT_EQ(r2->isolation_info().network_anonymization_key(), |
| network_anonymization_key2); |
| // Run request |
| r2->Start(); |
| d.RunUntilComplete(); |
| |
| EXPECT_THAT(d.request_status(), IsOk()); |
| // We should share a connection with r1 |
| EXPECT_EQ(SSLInfo::HANDSHAKE_RESUME, r2->ssl_info().handshake_type); |
| } |
| } |
| |
| TEST_F(PartitionConnectionsByNetworkAnonymizationKey, |
| DifferentNoncesAreAlwaysSeparated) { |
| // Start server |
| EmbeddedTestServer test_server(EmbeddedTestServer::TYPE_HTTPS); |
| RegisterDefaultHandlers(&test_server); |
| ASSERT_TRUE(test_server.Start()); |
| const auto original_url = test_server.GetURL("/echo"); |
| const auto network_anonymization_key1 = |
| NetworkAnonymizationKey::CreateFromParts( |
| kTestSiteA, /*is_cross_site=*/false, kNonceA); |
| const auto network_anonymization_key2 = |
| NetworkAnonymizationKey::CreateFromParts( |
| kTestSiteA, /*is_cross_site=*/false, kNonceB); |
| |
| // Create a request from first party `kTestSiteA`. |
| { |
| TestDelegate d; |
| std::unique_ptr<URLRequest> r1(default_context().CreateRequest( |
| original_url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| r1->set_isolation_info_from_network_anonymization_key( |
| network_anonymization_key1); |
| r1->SetLoadFlags(LOAD_DISABLE_CACHE); |
| r1->set_allow_credentials(false); |
| |
| // Verify NetworkAnonymizationKey is set correctly |
| EXPECT_TRUE(r1->is_created_from_network_anonymization_key()); |
| EXPECT_EQ(r1->isolation_info().network_anonymization_key(), |
| network_anonymization_key1); |
| // Run request |
| r1->Start(); |
| d.RunUntilComplete(); |
| // Verify request started with a full handshake |
| EXPECT_THAT(d.request_status(), IsOk()); |
| EXPECT_EQ(SSLInfo::HANDSHAKE_FULL, r1->ssl_info().handshake_type); |
| } |
| |
| // Create a request from third party `kTestSiteB` embedded in `kTestSiteA`. |
| // This request should share a key with r1 when NetworkAnonymizationKey is in |
| // double keyed scheme and should not share a key with r1 when |
| // NetworkAnonymizationKey is triple keyed or in cross site flag scheme. |
| { |
| TestDelegate d; |
| // Create request and create IsolationInfo from |
| // `network_anonymization_key2` |
| std::unique_ptr<URLRequest> r2(default_context().CreateRequest( |
| original_url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| r2->set_isolation_info_from_network_anonymization_key( |
| network_anonymization_key2); |
| r2->SetLoadFlags(LOAD_DISABLE_CACHE); |
| r2->set_allow_credentials(false); |
| |
| // Verify NetworkAnonymizationKey is set correctly. |
| EXPECT_TRUE(r2->is_created_from_network_anonymization_key()); |
| EXPECT_EQ(r2->isolation_info().network_anonymization_key(), |
| network_anonymization_key2); |
| // Run request |
| r2->Start(); |
| d.RunUntilComplete(); |
| |
| EXPECT_THAT(d.request_status(), IsOk()); |
| // Connections where the NetworkAnonymizationKey has different nonces should |
| // always be separated regardless of scheme |
| EXPECT_EQ(SSLInfo::HANDSHAKE_FULL, r2->ssl_info().handshake_type); |
| } |
| } |
| |
| } // namespace net |