blob: f16ed80b02b8d59d1c7d6ef259e26cee9cac59e7 [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <stdint.h>
#include <algorithm>
#include <iterator>
#include <limits>
#include <memory>
#include <utility>
#include "base/memory/raw_ptr.h"
// This must be before Windows headers
#include "base/callback_helpers.h"
#include "base/memory/ptr_util.h"
#include "build/build_config.h"
#include "net/dns/public/secure_dns_policy.h"
#include "net/log/net_log.h"
#include "url/url_constants.h"
#if BUILDFLAG(IS_WIN)
#include <objbase.h>
#include <shlobj.h>
#include <windows.h>
#include <wrl/client.h>
#endif
#include "base/base64url.h"
#include "base/bind.h"
#include "base/compiler_specific.h"
#include "base/cxx17_backports.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/format_macros.h"
#include "base/json/json_reader.h"
#include "base/location.h"
#include "base/memory/weak_ptr.h"
#include "base/path_service.h"
#include "base/run_loop.h"
#include "base/strings/strcat.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_piece.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "base/task/single_thread_task_runner.h"
#include "base/test/bind.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/values.h"
#include "build/buildflag.h"
#include "crypto/sha2.h"
#include "net/base/chunked_upload_data_stream.h"
#include "net/base/directory_listing.h"
#include "net/base/elements_upload_data_stream.h"
#include "net/base/escape.h"
#include "net/base/features.h"
#include "net/base/hash_value.h"
#include "net/base/ip_address.h"
#include "net/base/ip_endpoint.h"
#include "net/base/isolation_info.h"
#include "net/base/load_flags.h"
#include "net/base/load_timing_info.h"
#include "net/base/load_timing_info_test_util.h"
#include "net/base/net_errors.h"
#include "net/base/net_module.h"
#include "net/base/proxy_server.h"
#include "net/base/proxy_string_util.h"
#include "net/base/request_priority.h"
#include "net/base/test_completion_callback.h"
#include "net/base/transport_info.h"
#include "net/base/upload_bytes_element_reader.h"
#include "net/base/upload_data_stream.h"
#include "net/base/upload_file_element_reader.h"
#include "net/base/url_util.h"
#include "net/cert/asn1_util.h"
#include "net/cert/cert_net_fetcher.h"
#include "net/cert/crl_set.h"
#include "net/cert/ct_policy_enforcer.h"
#include "net/cert/ct_policy_status.h"
#include "net/cert/do_nothing_ct_verifier.h"
#include "net/cert/ev_root_ca_metadata.h"
#include "net/cert/mock_cert_verifier.h"
#include "net/cert/multi_log_ct_verifier.h"
#include "net/cert/signed_certificate_timestamp_and_status.h"
#include "net/cert/test_root_certs.h"
#include "net/cert/x509_util.h"
#include "net/cert_net/cert_net_fetcher_url_request.h"
#include "net/cookies/canonical_cookie_test_helpers.h"
#include "net/cookies/cookie_inclusion_status.h"
#include "net/cookies/cookie_monster.h"
#include "net/cookies/cookie_store_test_helpers.h"
#include "net/cookies/test_cookie_access_delegate.h"
#include "net/disk_cache/disk_cache.h"
#include "net/dns/mock_host_resolver.h"
#include "net/dns/public/secure_dns_policy.h"
#include "net/http/http_byte_range.h"
#include "net/http/http_cache.h"
#include "net/http/http_network_layer.h"
#include "net/http/http_network_session.h"
#include "net/http/http_request_headers.h"
#include "net/http/http_response_headers.h"
#include "net/http/http_server_properties.h"
#include "net/http/http_transaction_test_util.h"
#include "net/http/http_util.h"
#include "net/http/transport_security_state.h"
#include "net/http/transport_security_state_source.h"
#include "net/log/file_net_log_observer.h"
#include "net/log/net_log_event_type.h"
#include "net/log/net_log_source.h"
#include "net/log/test_net_log.h"
#include "net/log/test_net_log_util.h"
#include "net/net_buildflags.h"
#include "net/proxy_resolution/configured_proxy_resolution_service.h"
#include "net/quic/mock_crypto_client_stream_factory.h"
#include "net/quic/quic_server_info.h"
#include "net/socket/read_buffering_stream_socket.h"
#include "net/socket/socket_test_util.h"
#include "net/socket/ssl_client_socket.h"
#include "net/ssl/client_cert_identity_test_util.h"
#include "net/ssl/ssl_connection_status_flags.h"
#include "net/ssl/ssl_private_key.h"
#include "net/ssl/ssl_server_config.h"
#include "net/ssl/test_ssl_config_service.h"
#include "net/test/cert_test_util.h"
#include "net/test/embedded_test_server/http_request.h"
#include "net/test/embedded_test_server/http_response.h"
#include "net/test/gtest_util.h"
#include "net/test/spawned_test_server/spawned_test_server.h"
#include "net/test/test_data_directory.h"
#include "net/test/test_with_task_environment.h"
#include "net/test/url_request/url_request_failed_job.h"
#include "net/test/url_request/url_request_mock_http_job.h"
#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
#include "net/url_request/redirect_util.h"
#include "net/url_request/referrer_policy.h"
#include "net/url_request/static_http_user_agent_settings.h"
#include "net/url_request/url_request.h"
#include "net/url_request/url_request_context_builder.h"
#include "net/url_request/url_request_filter.h"
#include "net/url_request/url_request_http_job.h"
#include "net/url_request/url_request_interceptor.h"
#include "net/url_request/url_request_redirect_job.h"
#include "net/url_request/url_request_test_job.h"
#include "net/url_request/url_request_test_util.h"
#include "net/url_request/websocket_handshake_userdata_key.h"
#include "testing/gmock/include/gmock/gmock-matchers.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "testing/platform_test.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "third_party/boringssl/src/include/openssl/ssl.h"
#include "url/url_constants.h"
#include "url/url_util.h"
#if BUILDFLAG(IS_WIN)
#include "base/win/scoped_com_initializer.h"
#endif
#if BUILDFLAG(IS_APPLE)
#include "base/mac/mac_util.h"
#endif
#if BUILDFLAG(ENABLE_REPORTING)
#include "net/network_error_logging/network_error_logging_service.h"
#include "net/network_error_logging/network_error_logging_test_util.h"
#endif // BUILDFLAG(ENABLE_REPORTING)
#if BUILDFLAG(ENABLE_WEBSOCKETS)
#include "net/websockets/websocket_test_util.h"
#endif // BUILDFLAG(ENABLE_WEBSOCKETS)
using net::test::IsError;
using net::test::IsOk;
using net::test_server::RegisterDefaultHandlers;
using testing::_;
using testing::AnyOf;
using testing::ElementsAre;
using testing::IsEmpty;
using testing::Optional;
using testing::UnorderedElementsAre;
using base::ASCIIToUTF16;
using base::Time;
using std::string;
namespace net {
namespace {
namespace test_default {
#include "net/http/transport_security_state_static_unittest_default.h"
}
const std::u16string kChrome(u"chrome");
const std::u16string kSecret(u"secret");
const std::u16string kUser(u"user");
const base::FilePath::CharType kTestFilePath[] =
FILE_PATH_LITERAL("net/data/url_request_unittest");
// Tests load timing information in the case a fresh connection was used, with
// no proxy.
void TestLoadTimingNotReused(const LoadTimingInfo& load_timing_info,
int connect_timing_flags) {
EXPECT_FALSE(load_timing_info.socket_reused);
EXPECT_NE(NetLogSource::kInvalidId, load_timing_info.socket_log_id);
EXPECT_FALSE(load_timing_info.request_start_time.is_null());
EXPECT_FALSE(load_timing_info.request_start.is_null());
EXPECT_LE(load_timing_info.request_start,
load_timing_info.connect_timing.connect_start);
ExpectConnectTimingHasTimes(load_timing_info.connect_timing,
connect_timing_flags);
EXPECT_LE(load_timing_info.connect_timing.connect_end,
load_timing_info.send_start);
EXPECT_LE(load_timing_info.send_start, load_timing_info.send_end);
EXPECT_LE(load_timing_info.send_end, load_timing_info.receive_headers_start);
EXPECT_LE(load_timing_info.receive_headers_start,
load_timing_info.receive_headers_end);
EXPECT_TRUE(load_timing_info.proxy_resolve_start.is_null());
EXPECT_TRUE(load_timing_info.proxy_resolve_end.is_null());
}
// Same as above, but with proxy times.
void TestLoadTimingNotReusedWithProxy(const LoadTimingInfo& load_timing_info,
int connect_timing_flags) {
EXPECT_FALSE(load_timing_info.socket_reused);
EXPECT_NE(NetLogSource::kInvalidId, load_timing_info.socket_log_id);
EXPECT_FALSE(load_timing_info.request_start_time.is_null());
EXPECT_FALSE(load_timing_info.request_start.is_null());
EXPECT_LE(load_timing_info.request_start,
load_timing_info.proxy_resolve_start);
EXPECT_LE(load_timing_info.proxy_resolve_start,
load_timing_info.proxy_resolve_end);
EXPECT_LE(load_timing_info.proxy_resolve_end,
load_timing_info.connect_timing.connect_start);
ExpectConnectTimingHasTimes(load_timing_info.connect_timing,
connect_timing_flags);
EXPECT_LE(load_timing_info.connect_timing.connect_end,
load_timing_info.send_start);
EXPECT_LE(load_timing_info.send_start, load_timing_info.send_end);
EXPECT_LE(load_timing_info.send_end, load_timing_info.receive_headers_start);
EXPECT_LE(load_timing_info.receive_headers_start,
load_timing_info.receive_headers_end);
}
// Same as above, but with a reused socket and proxy times.
void TestLoadTimingReusedWithProxy(const LoadTimingInfo& load_timing_info) {
EXPECT_TRUE(load_timing_info.socket_reused);
EXPECT_NE(NetLogSource::kInvalidId, load_timing_info.socket_log_id);
EXPECT_FALSE(load_timing_info.request_start_time.is_null());
EXPECT_FALSE(load_timing_info.request_start.is_null());
ExpectConnectTimingHasNoTimes(load_timing_info.connect_timing);
EXPECT_LE(load_timing_info.request_start,
load_timing_info.proxy_resolve_start);
EXPECT_LE(load_timing_info.proxy_resolve_start,
load_timing_info.proxy_resolve_end);
EXPECT_LE(load_timing_info.proxy_resolve_end, load_timing_info.send_start);
EXPECT_LE(load_timing_info.send_start, load_timing_info.send_end);
EXPECT_LE(load_timing_info.send_end, load_timing_info.receive_headers_start);
EXPECT_LE(load_timing_info.receive_headers_start,
load_timing_info.receive_headers_end);
}
CookieList GetAllCookies(URLRequestContext* request_context) {
CookieList cookie_list;
base::RunLoop run_loop;
request_context->cookie_store()->GetAllCookiesAsync(
base::BindLambdaForTesting([&](const CookieList& cookies) {
cookie_list = cookies;
run_loop.Quit();
}));
run_loop.Run();
return cookie_list;
}
void TestLoadTimingCacheHitNoNetwork(const LoadTimingInfo& load_timing_info) {
EXPECT_FALSE(load_timing_info.socket_reused);
EXPECT_EQ(NetLogSource::kInvalidId, load_timing_info.socket_log_id);
EXPECT_FALSE(load_timing_info.request_start_time.is_null());
EXPECT_FALSE(load_timing_info.request_start.is_null());
ExpectConnectTimingHasNoTimes(load_timing_info.connect_timing);
EXPECT_LE(load_timing_info.request_start, load_timing_info.send_start);
EXPECT_LE(load_timing_info.send_start, load_timing_info.send_end);
EXPECT_LE(load_timing_info.send_end, load_timing_info.receive_headers_start);
EXPECT_LE(load_timing_info.receive_headers_start,
load_timing_info.receive_headers_end);
EXPECT_TRUE(load_timing_info.proxy_resolve_start.is_null());
EXPECT_TRUE(load_timing_info.proxy_resolve_end.is_null());
}
// Job that allows monitoring of its priority.
class PriorityMonitoringURLRequestJob : public URLRequestTestJob {
public:
// The latest priority of the job is always written to |request_priority_|.
PriorityMonitoringURLRequestJob(URLRequest* request,
RequestPriority* request_priority)
: URLRequestTestJob(request), request_priority_(request_priority) {
*request_priority_ = DEFAULT_PRIORITY;
}
void SetPriority(RequestPriority priority) override {
*request_priority_ = priority;
URLRequestTestJob::SetPriority(priority);
}
private:
const raw_ptr<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 = std::search(
haystack.begin(), haystack.end(), needle, needle + strlen(needle),
base::CaseInsensitiveCompareASCII<char>());
return it != haystack.end();
}
std::unique_ptr<UploadDataStream> CreateSimpleUploadData(const char* data) {
std::unique_ptr<UploadElementReader> reader(
new UploadBytesElementReader(data, strlen(data)));
return ElementsUploadDataStream::CreateWithReader(std::move(reader), 0);
}
// Verify that the SSLInfo of a successful SSL connection has valid values.
void CheckSSLInfo(const SSLInfo& ssl_info) {
// The cipher suite TLS_NULL_WITH_NULL_NULL (0) must not be negotiated.
uint16_t cipher_suite =
SSLConnectionStatusToCipherSuite(ssl_info.connection_status);
EXPECT_NE(0U, cipher_suite);
}
// A network delegate that allows the user to choose a subset of request stages
// to block in. When blocking, the delegate can do one of the following:
// * synchronously return a pre-specified error code, or
// * asynchronously return that value via an automatically called callback,
// or
// * block and wait for the user to do a callback.
// Additionally, the user may also specify a redirect URL -- then each request
// with the current URL different from the redirect target will be redirected
// to that target, in the on-before-URL-request stage, independent of whether
// the delegate blocks in ON_BEFORE_URL_REQUEST or not.
class BlockingNetworkDelegate : public TestNetworkDelegate {
public:
// Stages in which the delegate can block.
enum Stage {
NOT_BLOCKED = 0,
ON_BEFORE_URL_REQUEST = 1 << 0,
ON_BEFORE_SEND_HEADERS = 1 << 1,
ON_HEADERS_RECEIVED = 1 << 2,
};
// Behavior during blocked stages. During other stages, just
// returns OK or NetworkDelegate::AUTH_REQUIRED_RESPONSE_NO_ACTION.
enum BlockMode {
SYNCHRONOUS, // No callback, returns specified return values.
AUTO_CALLBACK, // |this| posts a task to run the callback using the
// specified return codes.
USER_CALLBACK, // User takes care of doing a callback. |retval_| and
// |auth_retval_| are ignored. In every blocking stage the
// message loop is quit.
};
// Creates a delegate which does not block at all.
explicit BlockingNetworkDelegate(BlockMode block_mode);
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_;
GURL redirect_url_; // Used if non-empty during OnBeforeURLRequest.
int block_on_; // Bit mask: in which stages to block.
// Internal variables, not set by not the user:
// Last blocked stage waiting for user callback (unused if |block_mode_| !=
// USER_CALLBACK).
Stage stage_blocked_for_callback_;
// Callback objects stored during blocking stages.
CompletionOnceCallback callback_;
// Closure to run to exit RunUntilBlocked().
base::OnceClosure on_blocked_;
base::WeakPtrFactory<BlockingNetworkDelegate> weak_factory_{this};
};
BlockingNetworkDelegate::BlockingNetworkDelegate(BlockMode block_mode)
: block_mode_(block_mode),
retval_(OK),
block_on_(0),
stage_blocked_for_callback_(NOT_BLOCKED) {}
void BlockingNetworkDelegate::RunUntilBlocked() {
base::RunLoop run_loop;
on_blocked_ = run_loop.QuitClosure();
run_loop.Run();
}
void BlockingNetworkDelegate::DoCallback(int response) {
ASSERT_EQ(USER_CALLBACK, block_mode_);
ASSERT_NE(NOT_BLOCKED, stage_blocked_for_callback_);
CompletionOnceCallback callback = std::move(callback_);
Reset();
// |callback| may trigger completion of a request, so post it as a task, so
// it will run under a subsequent TestDelegate::RunUntilComplete() loop.
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(&BlockingNetworkDelegate::RunCallback,
weak_factory_.GetWeakPtr(), response,
std::move(callback)));
}
void BlockingNetworkDelegate::OnBlocked() {
// If this fails due to |on_blocked_| being null then OnBlocked() was run by
// a RunLoop other than RunUntilBlocked(), indicating a bug in the calling
// test.
std::move(on_blocked_).Run();
}
void BlockingNetworkDelegate::RunCallback(int response,
CompletionOnceCallback callback) {
std::move(callback).Run(response);
}
int BlockingNetworkDelegate::OnBeforeURLRequest(URLRequest* request,
CompletionOnceCallback callback,
GURL* new_url) {
if (redirect_url_ == request->url())
return OK; // We've already seen this request and redirected elsewhere.
// TestNetworkDelegate always completes synchronously.
CHECK_NE(ERR_IO_PENDING, TestNetworkDelegate::OnBeforeURLRequest(
request, base::NullCallback(), new_url));
if (!redirect_url_.is_empty())
*new_url = redirect_url_;
return MaybeBlockStage(ON_BEFORE_URL_REQUEST, std::move(callback));
}
int BlockingNetworkDelegate::OnBeforeStartTransaction(
URLRequest* request,
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::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(&BlockingNetworkDelegate::RunCallback,
weak_factory_.GetWeakPtr(), retval_,
std::move(callback)));
return ERR_IO_PENDING;
case USER_CALLBACK:
callback_ = std::move(callback);
stage_blocked_for_callback_ = stage;
// We may reach here via a callback prior to RunUntilBlocked(), so post
// a task to fetch and run the |on_blocked_| closure.
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(&BlockingNetworkDelegate::OnBlocked,
weak_factory_.GetWeakPtr()));
return ERR_IO_PENDING;
}
NOTREACHED();
return 0;
}
// A mock ReportSenderInterface that just remembers the latest report
// URI and report to be sent.
class MockCertificateReportSender
: public TransportSecurityState::ReportSenderInterface {
public:
MockCertificateReportSender() = default;
~MockCertificateReportSender() override = default;
void Send(
const GURL& report_uri,
base::StringPiece content_type,
base::StringPiece report,
const NetworkIsolationKey& network_isolation_key,
base::OnceCallback<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_isolation_key_ = network_isolation_key;
}
const GURL& latest_report_uri() { return latest_report_uri_; }
const std::string& latest_report() { return latest_report_; }
const std::string& latest_content_type() { return latest_content_type_; }
const NetworkIsolationKey& latest_network_isolation_key() {
return latest_network_isolation_key_;
}
private:
GURL latest_report_uri_;
std::string latest_report_;
std::string latest_content_type_;
NetworkIsolationKey latest_network_isolation_key_;
};
// OCSPErrorTestDelegate caches the SSLInfo passed to OnSSLCertificateError.
// This is needed because after the certificate failure, the URLRequest will
// retry the connection, and return a partial SSLInfo with a cached cert status.
// The partial SSLInfo does not have the OCSP information filled out.
class OCSPErrorTestDelegate : public TestDelegate {
public:
void OnSSLCertificateError(URLRequest* request,
int net_error,
const SSLInfo& ssl_info,
bool fatal) override {
ssl_info_ = ssl_info;
on_ssl_certificate_error_called_ = true;
TestDelegate::OnSSLCertificateError(request, net_error, ssl_info, fatal);
}
bool on_ssl_certificate_error_called() {
return on_ssl_certificate_error_called_;
}
SSLInfo ssl_info() { return ssl_info_; }
private:
bool on_ssl_certificate_error_called_ = false;
SSLInfo ssl_info_;
};
#if !BUILDFLAG(IS_IOS)
// Compute the root cert's SPKI hash on the fly, to avoid hardcoding it within
// tests.
bool GetTestRootCertSPKIHash(SHA256HashValue* root_hash) {
scoped_refptr<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_EQ(static_cast<int>(data_size),
base::WriteFile(*test_file, data, data_size));
}
static std::unique_ptr<ConfiguredProxyResolutionService>
CreateFixedProxyResolutionService(const std::string& proxy) {
return ConfiguredProxyResolutionService::CreateFixed(
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.dns_start = now + base::Days(3);
connect_timing.dns_end = now + base::Days(4);
}
connect_timing.connect_start = now + base::Days(5);
if (connect_time_flags & CONNECT_TIMING_HAS_SSL_TIMES) {
connect_timing.ssl_start = now + base::Days(6);
connect_timing.ssl_end = now + base::Days(7);
}
connect_timing.connect_end = now + base::Days(8);
load_timing.send_start = now + base::Days(9);
load_timing.send_end = now + base::Days(10);
load_timing.receive_headers_start = now + base::Days(11);
load_timing.receive_headers_end = now + base::Days(12);
return load_timing;
}
// Same as above, but in the case of a reused socket.
LoadTimingInfo NormalLoadTimingInfoReused(base::TimeTicks now,
bool used_proxy) {
LoadTimingInfo load_timing;
load_timing.socket_log_id = 1;
load_timing.socket_reused = true;
if (used_proxy) {
load_timing.proxy_resolve_start = now + base::Days(1);
load_timing.proxy_resolve_end = now + base::Days(2);
}
load_timing.send_start = now + base::Days(9);
load_timing.send_end = now + base::Days(10);
load_timing.receive_headers_start = now + base::Days(11);
load_timing.receive_headers_end = now + base::Days(12);
return load_timing;
}
LoadTimingInfo RunURLRequestInterceptorLoadTimingTest(
const LoadTimingInfo& job_load_timing,
const URLRequestContext& context,
URLRequestInterceptorWithLoadTimingInfo* interceptor) {
interceptor->set_main_request_load_timing_info(job_load_timing);
TestDelegate d;
std::unique_ptr<URLRequest> req(
context.CreateRequest(GURL("http://test_intercept/foo"), DEFAULT_PRIORITY,
&d, TRAFFIC_ANNOTATION_FOR_TESTS));
req->Start();
d.RunUntilComplete();
LoadTimingInfo resulting_load_timing;
req->GetLoadTimingInfo(&resulting_load_timing);
// None of these should be modified by the URLRequest.
EXPECT_EQ(job_load_timing.socket_reused, resulting_load_timing.socket_reused);
EXPECT_EQ(job_load_timing.socket_log_id, resulting_load_timing.socket_log_id);
EXPECT_EQ(job_load_timing.send_start, resulting_load_timing.send_start);
EXPECT_EQ(job_load_timing.send_end, resulting_load_timing.send_end);
EXPECT_EQ(job_load_timing.receive_headers_start,
resulting_load_timing.receive_headers_start);
EXPECT_EQ(job_load_timing.receive_headers_end,
resulting_load_timing.receive_headers_end);
EXPECT_EQ(job_load_timing.push_start, resulting_load_timing.push_start);
EXPECT_EQ(job_load_timing.push_end, resulting_load_timing.push_end);
return resulting_load_timing;
}
// Basic test that the intercept + load timing tests work.
TEST_F(URLRequestLoadTimingTest, InterceptLoadTiming) {
base::TimeTicks now = base::TimeTicks::Now();
LoadTimingInfo job_load_timing =
NormalLoadTimingInfo(now, CONNECT_TIMING_HAS_DNS_TIMES, false);
LoadTimingInfo load_timing_result = RunURLRequestInterceptorLoadTimingTest(
job_load_timing, default_context(), interceptor());
// Nothing should have been changed by the URLRequest.
EXPECT_EQ(job_load_timing.proxy_resolve_start,
load_timing_result.proxy_resolve_start);
EXPECT_EQ(job_load_timing.proxy_resolve_end,
load_timing_result.proxy_resolve_end);
EXPECT_EQ(job_load_timing.connect_timing.dns_start,
load_timing_result.connect_timing.dns_start);
EXPECT_EQ(job_load_timing.connect_timing.dns_end,
load_timing_result.connect_timing.dns_end);
EXPECT_EQ(job_load_timing.connect_timing.connect_start,
load_timing_result.connect_timing.connect_start);
EXPECT_EQ(job_load_timing.connect_timing.connect_end,
load_timing_result.connect_timing.connect_end);
EXPECT_EQ(job_load_timing.connect_timing.ssl_start,
load_timing_result.connect_timing.ssl_start);
EXPECT_EQ(job_load_timing.connect_timing.ssl_end,
load_timing_result.connect_timing.ssl_end);
// Redundant sanity check.
TestLoadTimingNotReused(load_timing_result, CONNECT_TIMING_HAS_DNS_TIMES);
}
// Another basic test, with proxy and SSL times, but no DNS times.
TEST_F(URLRequestLoadTimingTest, InterceptLoadTimingProxy) {
base::TimeTicks now = base::TimeTicks::Now();
LoadTimingInfo job_load_timing =
NormalLoadTimingInfo(now, CONNECT_TIMING_HAS_SSL_TIMES, true);
LoadTimingInfo load_timing_result = RunURLRequestInterceptorLoadTimingTest(
job_load_timing, default_context(), interceptor());
// Nothing should have been changed by the URLRequest.
EXPECT_EQ(job_load_timing.proxy_resolve_start,
load_timing_result.proxy_resolve_start);
EXPECT_EQ(job_load_timing.proxy_resolve_end,
load_timing_result.proxy_resolve_end);
EXPECT_EQ(job_load_timing.connect_timing.dns_start,
load_timing_result.connect_timing.dns_start);
EXPECT_EQ(job_load_timing.connect_timing.dns_end,
load_timing_result.connect_timing.dns_end);
EXPECT_EQ(job_load_timing.connect_timing.connect_start,
load_timing_result.connect_timing.connect_start);
EXPECT_EQ(job_load_timing.connect_timing.connect_end,
load_timing_result.connect_timing.connect_end);
EXPECT_EQ(job_load_timing.connect_timing.ssl_start,
load_timing_result.connect_timing.ssl_start);
EXPECT_EQ(job_load_timing.connect_timing.ssl_end,
load_timing_result.connect_timing.ssl_end);
// Redundant sanity check.
TestLoadTimingNotReusedWithProxy(load_timing_result,
CONNECT_TIMING_HAS_SSL_TIMES);
}
// Make sure that URLRequest correctly adjusts proxy times when they're before
// |request_start|, due to already having a connected socket. This happens in
// the case of reusing a SPDY session. The connected socket is not considered
// reused in this test (May be a preconnect).
//
// To mix things up from the test above, assumes DNS times but no SSL times.
TEST_F(URLRequestLoadTimingTest, InterceptLoadTimingEarlyProxyResolution) {
base::TimeTicks now = base::TimeTicks::Now();
LoadTimingInfo job_load_timing =
NormalLoadTimingInfo(now, CONNECT_TIMING_HAS_DNS_TIMES, true);
job_load_timing.proxy_resolve_start = now - base::Days(6);
job_load_timing.proxy_resolve_end = now - base::Days(5);
job_load_timing.connect_timing.dns_start = now - base::Days(4);
job_load_timing.connect_timing.dns_end = now - base::Days(3);
job_load_timing.connect_timing.connect_start = now - base::Days(2);
job_load_timing.connect_timing.connect_end = now - base::Days(1);
LoadTimingInfo load_timing_result = RunURLRequestInterceptorLoadTimingTest(
job_load_timing, default_context(), interceptor());
// Proxy times, connect times, and DNS times should all be replaced with
// request_start.
EXPECT_EQ(load_timing_result.request_start,
load_timing_result.proxy_resolve_start);
EXPECT_EQ(load_timing_result.request_start,
load_timing_result.proxy_resolve_end);
EXPECT_EQ(load_timing_result.request_start,
load_timing_result.connect_timing.dns_start);
EXPECT_EQ(load_timing_result.request_start,
load_timing_result.connect_timing.dns_end);
EXPECT_EQ(load_timing_result.request_start,
load_timing_result.connect_timing.connect_start);
EXPECT_EQ(load_timing_result.request_start,
load_timing_result.connect_timing.connect_end);
// Other times should have been left null.
TestLoadTimingNotReusedWithProxy(load_timing_result,
CONNECT_TIMING_HAS_DNS_TIMES);
}
// Same as above, but in the reused case.
TEST_F(URLRequestLoadTimingTest,
InterceptLoadTimingEarlyProxyResolutionReused) {
base::TimeTicks now = base::TimeTicks::Now();
LoadTimingInfo job_load_timing = NormalLoadTimingInfoReused(now, true);
job_load_timing.proxy_resolve_start = now - base::Days(4);
job_load_timing.proxy_resolve_end = now - base::Days(3);
LoadTimingInfo load_timing_result = RunURLRequestInterceptorLoadTimingTest(
job_load_timing, default_context(), interceptor());
// Proxy times and connect times should all be replaced with request_start.
EXPECT_EQ(load_timing_result.request_start,
load_timing_result.proxy_resolve_start);
EXPECT_EQ(load_timing_result.request_start,
load_timing_result.proxy_resolve_end);
// Other times should have been left null.
TestLoadTimingReusedWithProxy(load_timing_result);
}
// Make sure that URLRequest correctly adjusts connect times when they're before
// |request_start|, due to reusing a connected socket. The connected socket is
// not considered reused in this test (May be a preconnect).
//
// To mix things up, the request has SSL times, but no DNS times.
TEST_F(URLRequestLoadTimingTest, InterceptLoadTimingEarlyConnect) {
base::TimeTicks now = base::TimeTicks::Now();
LoadTimingInfo job_load_timing =
NormalLoadTimingInfo(now, CONNECT_TIMING_HAS_SSL_TIMES, false);
job_load_timing.connect_timing.connect_start = now - base::Days(1);
job_load_timing.connect_timing.ssl_start = now - base::Days(2);
job_load_timing.connect_timing.ssl_end = now - base::Days(3);
job_load_timing.connect_timing.connect_end = now - base::Days(4);
LoadTimingInfo load_timing_result = RunURLRequestInterceptorLoadTimingTest(
job_load_timing, default_context(), interceptor());
// Connect times, and SSL times should be replaced with request_start.
EXPECT_EQ(load_timing_result.request_start,
load_timing_result.connect_timing.connect_start);
EXPECT_EQ(load_timing_result.request_start,
load_timing_result.connect_timing.ssl_start);
EXPECT_EQ(load_timing_result.request_start,
load_timing_result.connect_timing.ssl_end);
EXPECT_EQ(load_timing_result.request_start,
load_timing_result.connect_timing.connect_end);
// Other times should have been left null.
TestLoadTimingNotReused(load_timing_result, CONNECT_TIMING_HAS_SSL_TIMES);
}
// Make sure that URLRequest correctly adjusts connect times when they're before
// |request_start|, due to reusing a connected socket in the case that there
// are also proxy times. The connected socket is not considered reused in this
// test (May be a preconnect).
//
// In this test, there are no SSL or DNS times.
TEST_F(URLRequestLoadTimingTest, InterceptLoadTimingEarlyConnectWithProxy) {
base::TimeTicks now = base::TimeTicks::Now();
LoadTimingInfo job_load_timing =
NormalLoadTimingInfo(now, CONNECT_TIMING_HAS_CONNECT_TIMES_ONLY, true);
job_load_timing.connect_timing.connect_start = now - base::Days(1);
job_load_timing.connect_timing.connect_end = now - base::Days(2);
LoadTimingInfo load_timing_result = RunURLRequestInterceptorLoadTimingTest(
job_load_timing, default_context(), interceptor());
// Connect times should be replaced with proxy_resolve_end.
EXPECT_EQ(load_timing_result.proxy_resolve_end,
load_timing_result.connect_timing.connect_start);
EXPECT_EQ(load_timing_result.proxy_resolve_end,
load_timing_result.connect_timing.connect_end);
// Other times should have been left null.
TestLoadTimingNotReusedWithProxy(load_timing_result,
CONNECT_TIMING_HAS_CONNECT_TIMES_ONLY);
}
TEST_F(URLRequestTest, NetworkDelegateProxyError) {
auto context_builder = CreateTestURLRequestContextBuilder();
context_builder->set_proxy_resolution_service(
CreateFixedProxyResolutionService("myproxy:70"));
auto& network_delegate = *context_builder->set_network_delegate(
std::make_unique<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) {
base::test::ScopedFeatureList features;
features.InitAndEnableFeatureWithParameters(
features::kUseDnsHttpsSvcb, {{"UseDnsHttpsSvcbHttpUpgrade", "true"}});
EmbeddedTestServer https_server(EmbeddedTestServer::TYPE_HTTPS);
https_server.SetSSLConfig(EmbeddedTestServer::CERT_TEST_NAMES);
RegisterDefaultHandlers(&https_server);
ASSERT_TRUE(https_server.Start());
// Build an http URL that should be auto-upgraded to https.
const std::string kHost = "foo.a.test"; // Covered by CERT_TEST_NAMES.
const GURL https_url = https_server.GetURL(kHost, "/defaultresponse");
GURL::Replacements replacements;
replacements.SetSchemeStr(url::kHttpScheme);
const GURL http_url = https_url.ReplaceComponents(replacements);
// Return `ERR_DNS_NAME_HTTPS_ONLY` for "http://" requests and an address for
// "https://" requests. This simulates the HostResolver behavior for a domain
// with an HTTPS DNS record.
auto host_resolver = std::make_unique<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) {
base::test::ScopedFeatureList features;
features.InitAndEnableFeatureWithParameters(
features::kUseDnsHttpsSvcb, {{"UseDnsHttpsSvcbHttpUpgrade", "true"}});
EmbeddedTestServer https_server(EmbeddedTestServer::TYPE_HTTPS);
https_server.SetSSLConfig(EmbeddedTestServer::CERT_TEST_NAMES);
RegisterDefaultHandlers(&https_server);
ASSERT_TRUE(https_server.Start());
// Build an http URL that should be auto-upgraded to https.
const std::string kHost = "foo.a.test"; // Covered by CERT_TEST_NAMES.
const GURL https_url = https_server.GetURL(kHost, "/defaultresponse");
GURL::Replacements replacements;
replacements.SetSchemeStr(url::kHttpScheme);
const GURL http_url = https_url.ReplaceComponents(replacements);
// Return `ERR_DNS_NAME_HTTPS_ONLY` for "http://" requests and an address for
// "https://" requests. This simulates the HostResolver behavior for a domain
// with an HTTPS DNS record.
auto host_resolver = std::make_unique<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) {
base::test::ScopedFeatureList features;
features.InitAndEnableFeatureWithParameters(
features::kUseDnsHttpsSvcb, {{"UseDnsHttpsSvcbHttpUpgrade", "true"}});
EmbeddedTestServer https_server(EmbeddedTestServer::TYPE_HTTPS);
https_server.SetSSLConfig(EmbeddedTestServer::CERT_TEST_NAMES);
RegisterDefaultHandlers(&https_server);
ASSERT_TRUE(https_server.Start());
// Build an http URL that should be auto-upgraded to https.
const std::string kHost = "foo.a.test"; // Covered by CERT_TEST_NAMES.
const GURL https_url = https_server.GetURL(kHost, "/defaultresponse");
GURL::Replacements replacements;
replacements.SetSchemeStr(url::kWsScheme);
const GURL ws_url = https_url.ReplaceComponents(replacements);
auto host_resolver = std::make_unique<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) {
base::test::ScopedFeatureList features;
features.InitAndEnableFeatureWithParameters(
features::kUseDnsHttpsSvcb, {{"UseDnsHttpsSvcbHttpUpgrade", "true"}});
EmbeddedTestServer http_server(EmbeddedTestServer::TYPE_HTTP);
RegisterDefaultHandlers(&http_server);
ASSERT_TRUE(http_server.Start());
// Build an http URL that should be auto-upgraded to https.
const std::string kHost = "foo.a.test"; // Covered by CERT_TEST_NAMES.
const GURL http_url = http_server.GetURL(kHost, "/defaultresponse");
auto context_builder = CreateTestURLRequestContextBuilder();
auto host_resolver = std::make_unique<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,
/*first_party_sets_enabled=*/false);
cm->SetCanonicalCookieAsync(std::move(cookie2), url,
net::CookieOptions::MakeAllInclusive(),
CookieStore::SetCookiesCallback());
auto async_context_builder = CreateTestURLRequestContextBuilder();
async_context_builder->SetCookieStore(std::move(delayed_cm));
auto& async_filter_network_delegate =
*async_context_builder->set_network_delegate(
std::make_unique<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;