blob: 0e83d22d6621142a26d25458eff94a389da280f0 [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 <algorithm>
#include <utility>
// This must be before Windows headers
#include "base/bind_helpers.h"
#include "base/memory/ptr_util.h"
#include "build/build_config.h"
#if defined(OS_WIN)
#include <objbase.h>
#include <shlobj.h>
#include <windows.h>
#include <wrl/client.h>
#endif
#include <stdint.h>
#include <algorithm>
#include <limits>
#include <memory>
#include "base/base64url.h"
#include "base/bind.h"
#include "base/compiler_specific.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/format_macros.h"
#include "base/json/json_reader.h"
#include "base/location.h"
#include "base/memory/weak_ptr.h"
#include "base/optional.h"
#include "base/path_service.h"
#include "base/run_loop.h"
#include "base/single_thread_task_runner.h"
#include "base/stl_util.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_piece.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/bind_test_util.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/values.h"
#include "build/buildflag.h"
#include "crypto/sha2.h"
#include "net/base/chunked_upload_data_stream.h"
#include "net/base/directory_listing.h"
#include "net/base/elements_upload_data_stream.h"
#include "net/base/escape.h"
#include "net/base/features.h"
#include "net/base/hash_value.h"
#include "net/base/ip_address.h"
#include "net/base/ip_endpoint.h"
#include "net/base/isolation_info.h"
#include "net/base/load_flags.h"
#include "net/base/load_timing_info.h"
#include "net/base/load_timing_info_test_util.h"
#include "net/base/net_errors.h"
#include "net/base/net_module.h"
#include "net/base/proxy_server.h"
#include "net/base/request_priority.h"
#include "net/base/test_completion_callback.h"
#include "net/base/transport_info.h"
#include "net/base/upload_bytes_element_reader.h"
#include "net/base/upload_data_stream.h"
#include "net/base/upload_file_element_reader.h"
#include "net/base/url_util.h"
#include "net/cert/asn1_util.h"
#include "net/cert/cert_net_fetcher.h"
#include "net/cert/crl_set.h"
#include "net/cert/ct_policy_enforcer.h"
#include "net/cert/ct_policy_status.h"
#include "net/cert/do_nothing_ct_verifier.h"
#include "net/cert/ev_root_ca_metadata.h"
#include "net/cert/mock_cert_verifier.h"
#include "net/cert/multi_log_ct_verifier.h"
#include "net/cert/signed_certificate_timestamp_and_status.h"
#include "net/cert/test_root_certs.h"
#include "net/cert/x509_util.h"
#include "net/cert_net/cert_net_fetcher_url_request.h"
#include "net/cookies/canonical_cookie_test_helpers.h"
#include "net/cookies/cookie_inclusion_status.h"
#include "net/cookies/cookie_monster.h"
#include "net/cookies/cookie_store_test_helpers.h"
#include "net/cookies/test_cookie_access_delegate.h"
#include "net/disk_cache/disk_cache.h"
#include "net/dns/mock_host_resolver.h"
#include "net/http/http_byte_range.h"
#include "net/http/http_cache.h"
#include "net/http/http_network_layer.h"
#include "net/http/http_network_session.h"
#include "net/http/http_request_headers.h"
#include "net/http/http_response_headers.h"
#include "net/http/http_server_properties.h"
#include "net/http/http_util.h"
#include "net/log/file_net_log_observer.h"
#include "net/log/net_log_event_type.h"
#include "net/log/net_log_source.h"
#include "net/log/test_net_log.h"
#include "net/log/test_net_log_util.h"
#include "net/net_buildflags.h"
#include "net/proxy_resolution/configured_proxy_resolution_service.h"
#include "net/quic/mock_crypto_client_stream_factory.h"
#include "net/quic/quic_server_info.h"
#include "net/socket/read_buffering_stream_socket.h"
#include "net/socket/socket_test_util.h"
#include "net/socket/ssl_client_socket.h"
#include "net/ssl/client_cert_identity_test_util.h"
#include "net/ssl/ssl_connection_status_flags.h"
#include "net/ssl/ssl_private_key.h"
#include "net/ssl/ssl_server_config.h"
#include "net/ssl/test_ssl_config_service.h"
#include "net/test/cert_test_util.h"
#include "net/test/embedded_test_server/default_handlers.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "net/test/embedded_test_server/embedded_test_server_connection_listener.h"
#include "net/test/embedded_test_server/http_request.h"
#include "net/test/embedded_test_server/http_response.h"
#include "net/test/gtest_util.h"
#include "net/test/spawned_test_server/spawned_test_server.h"
#include "net/test/test_data_directory.h"
#include "net/test/test_with_task_environment.h"
#include "net/test/url_request/url_request_failed_job.h"
#include "net/test/url_request/url_request_mock_http_job.h"
#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
#include "net/url_request/referrer_policy.h"
#include "net/url_request/static_http_user_agent_settings.h"
#include "net/url_request/url_request.h"
#include "net/url_request/url_request_filter.h"
#include "net/url_request/url_request_http_job.h"
#include "net/url_request/url_request_interceptor.h"
#include "net/url_request/url_request_job_factory_impl.h"
#include "net/url_request/url_request_redirect_job.h"
#include "net/url_request/url_request_test_job.h"
#include "net/url_request/url_request_test_util.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "testing/platform_test.h"
#include "url/url_util.h"
#if !BUILDFLAG(DISABLE_FTP_SUPPORT) && !defined(OS_ANDROID)
#include "net/ftp/ftp_auth_cache.h"
#include "net/ftp/ftp_network_layer.h"
#include "net/url_request/ftp_protocol_handler.h"
#endif
#if defined(OS_WIN)
#include "base/win/scoped_com_initializer.h"
#endif
#if defined(OS_APPLE)
#include "base/mac/mac_util.h"
#endif
#if BUILDFLAG(ENABLE_REPORTING)
#include "net/network_error_logging/network_error_logging_service.h"
#include "net/network_error_logging/network_error_logging_test_util.h"
#endif // BUILDFLAG(ENABLE_REPORTING)
using net::test::IsError;
using net::test::IsOk;
using net::test_server::RegisterDefaultHandlers;
using testing::AnyOf;
using testing::ElementsAre;
using testing::IsEmpty;
using base::ASCIIToUTF16;
using base::Time;
using std::string;
namespace net {
namespace {
namespace test_default {
#include "net/http/transport_security_state_static_unittest_default.h"
}
const base::string16 kChrome(ASCIIToUTF16("chrome"));
const base::string16 kSecret(ASCIIToUTF16("secret"));
const base::string16 kUser(ASCIIToUTF16("user"));
const base::FilePath::CharType kTestFilePath[] =
FILE_PATH_LITERAL("net/data/url_request_unittest");
#if !BUILDFLAG(DISABLE_FTP_SUPPORT) && !defined(OS_ANDROID) && \
!defined(OS_FUCHSIA)
// Test file used in most FTP tests.
const char kFtpTestFile[] = "BullRunSpeech.txt";
#endif
// Tests load timing information in the case a fresh connection was used, with
// no proxy.
void TestLoadTimingNotReused(const LoadTimingInfo& load_timing_info,
int connect_timing_flags) {
EXPECT_FALSE(load_timing_info.socket_reused);
EXPECT_NE(NetLogSource::kInvalidId, load_timing_info.socket_log_id);
EXPECT_FALSE(load_timing_info.request_start_time.is_null());
EXPECT_FALSE(load_timing_info.request_start.is_null());
EXPECT_LE(load_timing_info.request_start,
load_timing_info.connect_timing.connect_start);
ExpectConnectTimingHasTimes(load_timing_info.connect_timing,
connect_timing_flags);
EXPECT_LE(load_timing_info.connect_timing.connect_end,
load_timing_info.send_start);
EXPECT_LE(load_timing_info.send_start, load_timing_info.send_end);
EXPECT_LE(load_timing_info.send_end, load_timing_info.receive_headers_start);
EXPECT_LE(load_timing_info.receive_headers_start,
load_timing_info.receive_headers_end);
EXPECT_TRUE(load_timing_info.proxy_resolve_start.is_null());
EXPECT_TRUE(load_timing_info.proxy_resolve_end.is_null());
}
// Same as above, but with proxy times.
void TestLoadTimingNotReusedWithProxy(const LoadTimingInfo& load_timing_info,
int connect_timing_flags) {
EXPECT_FALSE(load_timing_info.socket_reused);
EXPECT_NE(NetLogSource::kInvalidId, load_timing_info.socket_log_id);
EXPECT_FALSE(load_timing_info.request_start_time.is_null());
EXPECT_FALSE(load_timing_info.request_start.is_null());
EXPECT_LE(load_timing_info.request_start,
load_timing_info.proxy_resolve_start);
EXPECT_LE(load_timing_info.proxy_resolve_start,
load_timing_info.proxy_resolve_end);
EXPECT_LE(load_timing_info.proxy_resolve_end,
load_timing_info.connect_timing.connect_start);
ExpectConnectTimingHasTimes(load_timing_info.connect_timing,
connect_timing_flags);
EXPECT_LE(load_timing_info.connect_timing.connect_end,
load_timing_info.send_start);
EXPECT_LE(load_timing_info.send_start, load_timing_info.send_end);
EXPECT_LE(load_timing_info.send_end, load_timing_info.receive_headers_start);
EXPECT_LE(load_timing_info.receive_headers_start,
load_timing_info.receive_headers_end);
}
// Same as above, but with a reused socket and proxy times.
void TestLoadTimingReusedWithProxy(const LoadTimingInfo& load_timing_info) {
EXPECT_TRUE(load_timing_info.socket_reused);
EXPECT_NE(NetLogSource::kInvalidId, load_timing_info.socket_log_id);
EXPECT_FALSE(load_timing_info.request_start_time.is_null());
EXPECT_FALSE(load_timing_info.request_start.is_null());
ExpectConnectTimingHasNoTimes(load_timing_info.connect_timing);
EXPECT_LE(load_timing_info.request_start,
load_timing_info.proxy_resolve_start);
EXPECT_LE(load_timing_info.proxy_resolve_start,
load_timing_info.proxy_resolve_end);
EXPECT_LE(load_timing_info.proxy_resolve_end, load_timing_info.send_start);
EXPECT_LE(load_timing_info.send_start, load_timing_info.send_end);
EXPECT_LE(load_timing_info.send_end, load_timing_info.receive_headers_start);
EXPECT_LE(load_timing_info.receive_headers_start,
load_timing_info.receive_headers_end);
}
CookieList GetAllCookies(URLRequestContext* request_context) {
CookieList cookie_list;
base::RunLoop run_loop;
request_context->cookie_store()->GetAllCookiesAsync(
base::BindLambdaForTesting([&](const CookieList& cookies) {
cookie_list = cookies;
run_loop.Quit();
}));
run_loop.Run();
return cookie_list;
}
void TestLoadTimingCacheHitNoNetwork(const LoadTimingInfo& load_timing_info) {
EXPECT_FALSE(load_timing_info.socket_reused);
EXPECT_EQ(NetLogSource::kInvalidId, load_timing_info.socket_log_id);
EXPECT_FALSE(load_timing_info.request_start_time.is_null());
EXPECT_FALSE(load_timing_info.request_start.is_null());
ExpectConnectTimingHasNoTimes(load_timing_info.connect_timing);
EXPECT_LE(load_timing_info.request_start, load_timing_info.send_start);
EXPECT_LE(load_timing_info.send_start, load_timing_info.send_end);
EXPECT_LE(load_timing_info.send_end, load_timing_info.receive_headers_start);
EXPECT_LE(load_timing_info.receive_headers_start,
load_timing_info.receive_headers_end);
EXPECT_TRUE(load_timing_info.proxy_resolve_start.is_null());
EXPECT_TRUE(load_timing_info.proxy_resolve_end.is_null());
}
#if !BUILDFLAG(DISABLE_FTP_SUPPORT) && !defined(OS_ANDROID) && \
!defined(OS_FUCHSIA)
// Tests load timing in the case that there is no HTTP response. This can be
// used to test in the case of errors or non-HTTP requests.
void TestLoadTimingNoHttpResponse(const LoadTimingInfo& load_timing_info) {
EXPECT_FALSE(load_timing_info.socket_reused);
EXPECT_EQ(NetLogSource::kInvalidId, load_timing_info.socket_log_id);
// Only the request times should be non-null.
EXPECT_FALSE(load_timing_info.request_start_time.is_null());
EXPECT_FALSE(load_timing_info.request_start.is_null());
ExpectConnectTimingHasNoTimes(load_timing_info.connect_timing);
EXPECT_TRUE(load_timing_info.proxy_resolve_start.is_null());
EXPECT_TRUE(load_timing_info.proxy_resolve_end.is_null());
EXPECT_TRUE(load_timing_info.send_start.is_null());
EXPECT_TRUE(load_timing_info.send_end.is_null());
EXPECT_TRUE(load_timing_info.receive_headers_start.is_null());
EXPECT_TRUE(load_timing_info.receive_headers_end.is_null());
}
#endif
// Less verbose way of running a simple testserver for the tests below.
class HttpTestServer : public EmbeddedTestServer {
public:
explicit HttpTestServer(const base::FilePath& document_root) {
AddDefaultHandlers(document_root);
}
HttpTestServer() { RegisterDefaultHandlers(this); }
};
// Job that allows monitoring of its priority.
class PriorityMonitoringURLRequestJob : public URLRequestTestJob {
public:
// The latest priority of the job is always written to |request_priority_|.
PriorityMonitoringURLRequestJob(URLRequest* request,
NetworkDelegate* network_delegate,
RequestPriority* request_priority)
: URLRequestTestJob(request, network_delegate),
request_priority_(request_priority) {
*request_priority_ = DEFAULT_PRIORITY;
}
void SetPriority(RequestPriority priority) override {
*request_priority_ = priority;
URLRequestTestJob::SetPriority(priority);
}
private:
RequestPriority* const request_priority_;
};
// Do a case-insensitive search through |haystack| for |needle|.
bool ContainsString(const std::string& haystack, const char* needle) {
std::string::const_iterator it = std::search(
haystack.begin(), haystack.end(), needle, needle + strlen(needle),
base::CaseInsensitiveCompareASCII<char>());
return it != haystack.end();
}
std::unique_ptr<UploadDataStream> CreateSimpleUploadData(const char* data) {
std::unique_ptr<UploadElementReader> reader(
new UploadBytesElementReader(data, strlen(data)));
return ElementsUploadDataStream::CreateWithReader(std::move(reader), 0);
}
// Verify that the SSLInfo of a successful SSL connection has valid values.
void CheckSSLInfo(const SSLInfo& ssl_info) {
// The cipher suite TLS_NULL_WITH_NULL_NULL (0) must not be negotiated.
uint16_t cipher_suite =
SSLConnectionStatusToCipherSuite(ssl_info.connection_status);
EXPECT_NE(0U, cipher_suite);
}
// A network delegate that allows the user to choose a subset of request stages
// to block in. When blocking, the delegate can do one of the following:
// * synchronously return a pre-specified error code, or
// * asynchronously return that value via an automatically called callback,
// or
// * block and wait for the user to do a callback.
// Additionally, the user may also specify a redirect URL -- then each request
// with the current URL different from the redirect target will be redirected
// to that target, in the on-before-URL-request stage, independent of whether
// the delegate blocks in ON_BEFORE_URL_REQUEST or not.
class BlockingNetworkDelegate : public TestNetworkDelegate {
public:
// Stages in which the delegate can block.
enum Stage {
NOT_BLOCKED = 0,
ON_BEFORE_URL_REQUEST = 1 << 0,
ON_BEFORE_SEND_HEADERS = 1 << 1,
ON_HEADERS_RECEIVED = 1 << 2,
};
// Behavior during blocked stages. During other stages, just
// returns OK or NetworkDelegate::AUTH_REQUIRED_RESPONSE_NO_ACTION.
enum BlockMode {
SYNCHRONOUS, // No callback, returns specified return values.
AUTO_CALLBACK, // |this| posts a task to run the callback using the
// specified return codes.
USER_CALLBACK, // User takes care of doing a callback. |retval_| and
// |auth_retval_| are ignored. In every blocking stage the
// message loop is quit.
};
// Creates a delegate which does not block at all.
explicit BlockingNetworkDelegate(BlockMode block_mode);
// Runs the message loop until the delegate blocks.
void RunUntilBlocked();
// For users to trigger a callback returning |response|.
// Side-effects: resets |stage_blocked_for_callback_| and stored callbacks.
// Only call if |block_mode_| == USER_CALLBACK.
void DoCallback(int response);
// Setters.
void set_retval(int retval) {
ASSERT_NE(USER_CALLBACK, block_mode_);
ASSERT_NE(ERR_IO_PENDING, retval);
ASSERT_NE(OK, retval);
retval_ = retval;
}
void set_redirect_url(const GURL& url) { redirect_url_ = url; }
void set_block_on(int block_on) { block_on_ = block_on; }
// Allows the user to check in which state did we block.
Stage stage_blocked_for_callback() const {
EXPECT_EQ(USER_CALLBACK, block_mode_);
return stage_blocked_for_callback_;
}
private:
void OnBlocked();
void RunCallback(int response, CompletionOnceCallback callback);
// TestNetworkDelegate implementation.
int OnBeforeURLRequest(URLRequest* request,
CompletionOnceCallback callback,
GURL* new_url) override;
int OnBeforeStartTransaction(URLRequest* request,
CompletionOnceCallback callback,
HttpRequestHeaders* headers) override;
int OnHeadersReceived(
URLRequest* request,
CompletionOnceCallback callback,
const HttpResponseHeaders* original_response_headers,
scoped_refptr<HttpResponseHeaders>* override_response_headers,
const IPEndPoint& endpoint,
base::Optional<GURL>* preserve_fragment_on_redirect_url) override;
// Resets the callbacks and |stage_blocked_for_callback_|.
void Reset();
// Checks whether we should block in |stage|. If yes, returns an error code
// and optionally sets up callback based on |block_mode_|. If no, returns OK.
int MaybeBlockStage(Stage stage, CompletionOnceCallback callback);
// Configuration parameters, can be adjusted by public methods:
const BlockMode block_mode_;
// Values returned on blocking stages when mode is SYNCHRONOUS or
// AUTO_CALLBACK. For USER_CALLBACK these are set automatically to IO_PENDING.
int retval_;
GURL redirect_url_; // Used if non-empty during OnBeforeURLRequest.
int block_on_; // Bit mask: in which stages to block.
// Internal variables, not set by not the user:
// Last blocked stage waiting for user callback (unused if |block_mode_| !=
// USER_CALLBACK).
Stage stage_blocked_for_callback_;
// Callback objects stored during blocking stages.
CompletionOnceCallback callback_;
// Closure to run to exit RunUntilBlocked().
base::OnceClosure on_blocked_;
base::WeakPtrFactory<BlockingNetworkDelegate> weak_factory_{this};
DISALLOW_COPY_AND_ASSIGN(BlockingNetworkDelegate);
};
BlockingNetworkDelegate::BlockingNetworkDelegate(BlockMode block_mode)
: block_mode_(block_mode),
retval_(OK),
block_on_(0),
stage_blocked_for_callback_(NOT_BLOCKED) {}
void BlockingNetworkDelegate::RunUntilBlocked() {
base::RunLoop run_loop;
on_blocked_ = run_loop.QuitClosure();
run_loop.Run();
}
void BlockingNetworkDelegate::DoCallback(int response) {
ASSERT_EQ(USER_CALLBACK, block_mode_);
ASSERT_NE(NOT_BLOCKED, stage_blocked_for_callback_);
CompletionOnceCallback callback = std::move(callback_);
Reset();
// |callback| may trigger completion of a request, so post it as a task, so
// it will run under a subsequent TestDelegate::RunUntilComplete() loop.
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(&BlockingNetworkDelegate::RunCallback,
weak_factory_.GetWeakPtr(), response,
std::move(callback)));
}
void BlockingNetworkDelegate::OnBlocked() {
// If this fails due to |on_blocked_| being null then OnBlocked() was run by
// a RunLoop other than RunUntilBlocked(), indicating a bug in the calling
// test.
std::move(on_blocked_).Run();
}
void BlockingNetworkDelegate::RunCallback(int response,
CompletionOnceCallback callback) {
std::move(callback).Run(response);
}
int BlockingNetworkDelegate::OnBeforeURLRequest(URLRequest* request,
CompletionOnceCallback callback,
GURL* new_url) {
if (redirect_url_ == request->url())
return OK; // We've already seen this request and redirected elsewhere.
// TestNetworkDelegate always completes synchronously.
CHECK_NE(ERR_IO_PENDING, TestNetworkDelegate::OnBeforeURLRequest(
request, base::NullCallback(), new_url));
if (!redirect_url_.is_empty())
*new_url = redirect_url_;
return MaybeBlockStage(ON_BEFORE_URL_REQUEST, std::move(callback));
}
int BlockingNetworkDelegate::OnBeforeStartTransaction(
URLRequest* request,
CompletionOnceCallback callback,
HttpRequestHeaders* headers) {
// TestNetworkDelegate always completes synchronously.
CHECK_NE(ERR_IO_PENDING, TestNetworkDelegate::OnBeforeStartTransaction(
request, base::NullCallback(), headers));
return MaybeBlockStage(ON_BEFORE_SEND_HEADERS, std::move(callback));
}
int BlockingNetworkDelegate::OnHeadersReceived(
URLRequest* request,
CompletionOnceCallback callback,
const HttpResponseHeaders* original_response_headers,
scoped_refptr<HttpResponseHeaders>* override_response_headers,
const IPEndPoint& endpoint,
base::Optional<GURL>* preserve_fragment_on_redirect_url) {
// TestNetworkDelegate always completes synchronously.
CHECK_NE(ERR_IO_PENDING,
TestNetworkDelegate::OnHeadersReceived(
request, base::NullCallback(), original_response_headers,
override_response_headers, endpoint,
preserve_fragment_on_redirect_url));
return MaybeBlockStage(ON_HEADERS_RECEIVED, std::move(callback));
}
void BlockingNetworkDelegate::Reset() {
EXPECT_NE(NOT_BLOCKED, stage_blocked_for_callback_);
stage_blocked_for_callback_ = NOT_BLOCKED;
callback_.Reset();
}
int BlockingNetworkDelegate::MaybeBlockStage(
BlockingNetworkDelegate::Stage stage,
CompletionOnceCallback callback) {
// Check that the user has provided callback for the previous blocked stage.
EXPECT_EQ(NOT_BLOCKED, stage_blocked_for_callback_);
if ((block_on_ & stage) == 0) {
return OK;
}
switch (block_mode_) {
case SYNCHRONOUS:
EXPECT_NE(OK, retval_);
return retval_;
case AUTO_CALLBACK:
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(&BlockingNetworkDelegate::RunCallback,
weak_factory_.GetWeakPtr(), retval_,
std::move(callback)));
return ERR_IO_PENDING;
case USER_CALLBACK:
callback_ = std::move(callback);
stage_blocked_for_callback_ = stage;
// We may reach here via a callback prior to RunUntilBlocked(), so post
// a task to fetch and run the |on_blocked_| closure.
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(&BlockingNetworkDelegate::OnBlocked,
weak_factory_.GetWeakPtr()));
return ERR_IO_PENDING;
}
NOTREACHED();
return 0;
}
class TestURLRequestContextWithProxy : public TestURLRequestContext {
public:
// Does not own |delegate|.
TestURLRequestContextWithProxy(const std::string& proxy,
NetworkDelegate* delegate,
bool delay_initialization = false)
: TestURLRequestContext(true) {
context_storage_.set_proxy_resolution_service(
ConfiguredProxyResolutionService::CreateFixed(
proxy, TRAFFIC_ANNOTATION_FOR_TESTS));
set_network_delegate(delegate);
if (!delay_initialization)
Init();
}
~TestURLRequestContextWithProxy() override = default;
};
// A mock ReportSenderInterface that just remembers the latest report
// URI and report to be sent.
class MockCertificateReportSender
: public TransportSecurityState::ReportSenderInterface {
public:
MockCertificateReportSender() = default;
~MockCertificateReportSender() override = default;
void Send(
const GURL& report_uri,
base::StringPiece content_type,
base::StringPiece report,
base::OnceCallback<void()> success_callback,
base::OnceCallback<void(const GURL&, int, int)> error_callback) override {
latest_report_uri_ = report_uri;
latest_report_.assign(report.data(), report.size());
latest_content_type_.assign(content_type.data(), content_type.size());
}
const GURL& latest_report_uri() { return latest_report_uri_; }
const std::string& latest_report() { return latest_report_; }
const std::string& latest_content_type() { return latest_content_type_; }
private:
GURL latest_report_uri_;
std::string latest_report_;
std::string latest_content_type_;
};
// OCSPErrorTestDelegate caches the SSLInfo passed to OnSSLCertificateError.
// This is needed because after the certificate failure, the URLRequest will
// retry the connection, and return a partial SSLInfo with a cached cert status.
// The partial SSLInfo does not have the OCSP information filled out.
class OCSPErrorTestDelegate : public TestDelegate {
public:
void OnSSLCertificateError(URLRequest* request,
int net_error,
const SSLInfo& ssl_info,
bool fatal) override {
ssl_info_ = ssl_info;
on_ssl_certificate_error_called_ = true;
TestDelegate::OnSSLCertificateError(request, net_error, ssl_info, fatal);
}
bool on_ssl_certificate_error_called() {
return on_ssl_certificate_error_called_;
}
SSLInfo ssl_info() { return ssl_info_; }
private:
bool on_ssl_certificate_error_called_ = false;
SSLInfo ssl_info_;
};
} // namespace
// Inherit PlatformTest since we require the autorelease pool on Mac OS X.
class URLRequestTest : public PlatformTest, public WithTaskEnvironment {
public:
URLRequestTest()
: default_context_(std::make_unique<TestURLRequestContext>(true)) {
default_context_->set_network_delegate(&default_network_delegate_);
default_context_->set_net_log(&net_log_);
job_factory_impl_ = new URLRequestJobFactoryImpl();
job_factory_.reset(job_factory_impl_);
}
~URLRequestTest() override {
// URLRequestJobs may post clean-up tasks on destruction.
base::RunLoop().RunUntilIdle();
SetTransportSecurityStateSourceForTesting(nullptr);
}
void SetUp() override {
SetUpFactory();
default_context_->set_job_factory(job_factory_.get());
default_context_->Init();
PlatformTest::SetUp();
}
void TearDown() override { default_context_.reset(); }
virtual void SetUpFactory() {}
TestNetworkDelegate* default_network_delegate() {
return &default_network_delegate_;
}
TestURLRequestContext& default_context() const { return *default_context_; }
// Adds the TestJobInterceptor to the default context.
TestJobInterceptor* AddTestInterceptor() {
TestJobInterceptor* protocol_handler_ = new TestJobInterceptor();
job_factory_impl_->SetProtocolHandler("http", nullptr);
job_factory_impl_->SetProtocolHandler("http",
base::WrapUnique(protocol_handler_));
return protocol_handler_;
}
// Creates a temp test file and writes |data| to the file. The file will be
// deleted after the test completes.
void CreateTestFile(const char* data,
size_t data_size,
base::FilePath* test_file) {
ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
// Get an absolute path since |temp_dir| can contain a symbolic link. As of
// now, Mac and Android bots return a path with a symbolic link.
base::FilePath absolute_temp_dir =
base::MakeAbsoluteFilePath(temp_dir_.GetPath());
ASSERT_TRUE(base::CreateTemporaryFileInDir(absolute_temp_dir, test_file));
ASSERT_EQ(static_cast<int>(data_size),
base::WriteFile(*test_file, data, data_size));
}
protected:
RecordingTestNetLog net_log_;
TestNetworkDelegate default_network_delegate_; // Must outlive URLRequest.
URLRequestJobFactoryImpl* job_factory_impl_;
std::unique_ptr<URLRequestJobFactory> job_factory_;
std::unique_ptr<TestURLRequestContext> default_context_;
base::ScopedTempDir temp_dir_;
};
TEST_F(URLRequestTest, AboutBlankTest) {
TestDelegate d;
{
std::unique_ptr<URLRequest> r(
default_context().CreateRequest(GURL("about:blank"), DEFAULT_PRIORITY,
&d, TRAFFIC_ANNOTATION_FOR_TESTS));
r->Start();
EXPECT_TRUE(r->is_pending());
d.RunUntilComplete();
EXPECT_TRUE(!r->is_pending());
EXPECT_FALSE(d.received_data_before_response());
EXPECT_EQ(d.bytes_received(), 0);
EXPECT_TRUE(r->GetResponseRemoteEndpoint().address().empty());
EXPECT_EQ(0, r->GetResponseRemoteEndpoint().port());
}
}
TEST_F(URLRequestTest, InvalidUrlTest) {
TestDelegate d;
{
std::unique_ptr<URLRequest> r(
default_context().CreateRequest(GURL("invalid url"), DEFAULT_PRIORITY,
&d, TRAFFIC_ANNOTATION_FOR_TESTS));
r->Start();
EXPECT_TRUE(r->is_pending());
d.RunUntilComplete();
EXPECT_TRUE(d.request_failed());
}
}
TEST_F(URLRequestTest, InvalidReferrerTest) {
TestURLRequestContext context;
TestNetworkDelegate network_delegate;
network_delegate.set_cancel_request_with_policy_violating_referrer(true);
context.set_network_delegate(&network_delegate);
TestDelegate d;
std::unique_ptr<URLRequest> req(
context.CreateRequest(GURL("http://localhost/"), DEFAULT_PRIORITY, &d,
TRAFFIC_ANNOTATION_FOR_TESTS));
req->SetReferrer("https://somewhere.com/");
req->Start();
d.RunUntilComplete();
EXPECT_TRUE(d.request_failed());
}
TEST_F(URLRequestTest, RecordsSameOriginReferrerHistogram) {
TestURLRequestContext context;
TestNetworkDelegate network_delegate;
network_delegate.set_cancel_request_with_policy_violating_referrer(false);
context.set_network_delegate(&network_delegate);
TestDelegate d;
std::unique_ptr<URLRequest> req(
context.CreateRequest(GURL("http://google.com/"), DEFAULT_PRIORITY, &d,
TRAFFIC_ANNOTATION_FOR_TESTS));
req->SetReferrer("http://google.com");
req->set_referrer_policy(ReferrerPolicy::NEVER_CLEAR);
base::HistogramTester histograms;
req->Start();
d.RunUntilComplete();
histograms.ExpectUniqueSample(
"Net.URLRequest.ReferrerPolicyForRequest.SameOrigin",
static_cast<int>(ReferrerPolicy::NEVER_CLEAR), 1);
}
TEST_F(URLRequestTest, RecordsCrossOriginReferrerHistogram) {
TestURLRequestContext context;
TestNetworkDelegate network_delegate;
context.set_network_delegate(&network_delegate);
TestDelegate d;
std::unique_ptr<URLRequest> req(
context.CreateRequest(GURL("http://google.com/"), DEFAULT_PRIORITY, &d,
TRAFFIC_ANNOTATION_FOR_TESTS));
req->SetReferrer("http://origin.com");
// Set a different policy just to make sure we aren't always logging the same
// policy.
req->set_referrer_policy(
ReferrerPolicy::CLEAR_ON_TRANSITION_FROM_SECURE_TO_INSECURE);
base::HistogramTester histograms;
req->Start();
d.RunUntilComplete();
histograms.ExpectUniqueSample(
"Net.URLRequest.ReferrerPolicyForRequest.CrossOrigin",
static_cast<int>(
ReferrerPolicy::CLEAR_ON_TRANSITION_FROM_SECURE_TO_INSECURE),
1);
}
TEST_F(URLRequestTest, RecordsReferrerHistogramAgainOnRedirect) {
TestURLRequestContext context;
BlockingNetworkDelegate network_delegate(
BlockingNetworkDelegate::SYNCHRONOUS);
network_delegate.set_redirect_url(GURL("http://redirect.com/"));
context.set_network_delegate(&network_delegate);
TestDelegate d;
std::unique_ptr<URLRequest> req(
context.CreateRequest(GURL("http://google.com/"), DEFAULT_PRIORITY, &d,
TRAFFIC_ANNOTATION_FOR_TESTS));
req->SetReferrer("http://google.com");
req->set_referrer_policy(
ReferrerPolicy::CLEAR_ON_TRANSITION_FROM_SECURE_TO_INSECURE);
base::HistogramTester histograms;
req->Start();
d.RunUntilRedirect();
histograms.ExpectUniqueSample(
"Net.URLRequest.ReferrerPolicyForRequest.SameOrigin",
static_cast<int>(
ReferrerPolicy::CLEAR_ON_TRANSITION_FROM_SECURE_TO_INSECURE),
1);
req->FollowDeferredRedirect(/*removed_headers=*/base::nullopt,
/*modified_headers=*/base::nullopt);
d.RunUntilComplete();
histograms.ExpectUniqueSample(
"Net.URLRequest.ReferrerPolicyForRequest.CrossOrigin",
static_cast<int>(
ReferrerPolicy::CLEAR_ON_TRANSITION_FROM_SECURE_TO_INSECURE),
1);
}
TEST_F(URLRequestTest, RecordsReferrrerWithInformativePath) {
TestURLRequestContext context;
BlockingNetworkDelegate network_delegate(
BlockingNetworkDelegate::SYNCHRONOUS);
network_delegate.set_cancel_request_with_policy_violating_referrer(true);
context.set_network_delegate(&network_delegate);
network_delegate.set_redirect_url(GURL("http://redirect.com/"));
TestDelegate d;
std::unique_ptr<URLRequest> req(
context.CreateRequest(GURL("http://google.com/"), DEFAULT_PRIORITY, &d,
TRAFFIC_ANNOTATION_FOR_TESTS));
// Since this referrer is much more informative than the initiating origin,
// we should see the histograms' true buckets populated.
req->SetReferrer("http://google.com/very-informative-path");
base::HistogramTester histograms;
req->Start();
d.RunUntilRedirect();
histograms.ExpectUniqueSample(
"Net.URLRequest.ReferrerHasInformativePath.SameOrigin",
/* Check the count of the "true" bucket in the boolean histogram. */ true,
1);
req->FollowDeferredRedirect(/*removed_headers=*/base::nullopt,
/*modified_headers=*/base::nullopt);
d.RunUntilComplete();
histograms.ExpectUniqueSample(
"Net.URLRequest.ReferrerHasInformativePath.CrossOrigin", true, 1);
}
TEST_F(URLRequestTest, RecordsReferrerWithInformativeQuery) {
TestURLRequestContext context;
BlockingNetworkDelegate network_delegate(
BlockingNetworkDelegate::SYNCHRONOUS);
network_delegate.set_cancel_request_with_policy_violating_referrer(true);
context.set_network_delegate(&network_delegate);
network_delegate.set_redirect_url(GURL("http://redirect.com/"));
TestDelegate d;
std::unique_ptr<URLRequest> req(
context.CreateRequest(GURL("http://google.com/"), DEFAULT_PRIORITY, &d,
TRAFFIC_ANNOTATION_FOR_TESTS));
// Since this referrer is much more informative than the initiating origin,
// we should see the histograms' true buckets populated.
req->SetReferrer("http://google.com/?very-informative-query");
base::HistogramTester histograms;
req->Start();
d.RunUntilRedirect();
histograms.ExpectUniqueSample(
"Net.URLRequest.ReferrerHasInformativePath.SameOrigin",
/* Check the count of the "true" bucket in the boolean histogram. */ true,
1);
req->FollowDeferredRedirect(/*removed_headers=*/base::nullopt,
/*modified_headers=*/base::nullopt);
d.RunUntilComplete();
histograms.ExpectUniqueSample(
"Net.URLRequest.ReferrerHasInformativePath.CrossOrigin", true, 1);
}
TEST_F(URLRequestTest, RecordsReferrerWithoutInformativePathOrQuery) {
TestURLRequestContext context;
BlockingNetworkDelegate network_delegate(
BlockingNetworkDelegate::SYNCHRONOUS);
network_delegate.set_cancel_request_with_policy_violating_referrer(false);
context.set_network_delegate(&network_delegate);
network_delegate.set_redirect_url(GURL("http://origin.com/"));
TestDelegate d;
std::unique_ptr<URLRequest> req(
context.CreateRequest(GURL("http://google.com/"), DEFAULT_PRIORITY, &d,
TRAFFIC_ANNOTATION_FOR_TESTS));
// Since this referrer _isn't_ more informative than the initiating origin,
// we should see the histograms' false buckets populated.
req->SetReferrer("http://origin.com");
base::HistogramTester histograms;
req->Start();
d.RunUntilRedirect();
histograms.ExpectUniqueSample(
"Net.URLRequest.ReferrerHasInformativePath.CrossOrigin", false, 1);
req->FollowDeferredRedirect(/*removed_headers=*/base::nullopt,
/*modified_headers=*/base::nullopt);
d.RunUntilComplete();
histograms.ExpectUniqueSample(
"Net.URLRequest.ReferrerHasInformativePath.SameOrigin", false, 1);
}
// A URLRequestInterceptor that allows setting the LoadTimingInfo value of the
// URLRequestJobs it creates.
class URLRequestInterceptorWithLoadTimingInfo : public URLRequestInterceptor {
public:
// Static getters for canned response header and data strings.
static std::string ok_data() { return URLRequestTestJob::test_data_1(); }
static std::string ok_headers() { return URLRequestTestJob::test_headers(); }
URLRequestInterceptorWithLoadTimingInfo() = default;
~URLRequestInterceptorWithLoadTimingInfo() override = default;
// URLRequestInterceptor implementation:
URLRequestJob* MaybeInterceptRequest(
URLRequest* request,
NetworkDelegate* network_delegate) const override {
URLRequestTestJob* job = new URLRequestTestJob(
request, network_delegate, ok_headers(), ok_data(), true);
job->set_load_timing_info(main_request_load_timing_info_);
return job;
}
void set_main_request_load_timing_info(
const LoadTimingInfo& main_request_load_timing_info) {
main_request_load_timing_info_ = main_request_load_timing_info;
}
private:
mutable LoadTimingInfo main_request_load_timing_info_;
};
// These tests inject a MockURLRequestInterceptor
class URLRequestLoadTimingTest : public URLRequestTest {
public:
URLRequestLoadTimingTest() {
std::unique_ptr<URLRequestInterceptorWithLoadTimingInfo> interceptor =
std::make_unique<URLRequestInterceptorWithLoadTimingInfo>();
interceptor_ = interceptor.get();
URLRequestFilter::GetInstance()->AddHostnameInterceptor(
"http", "test_intercept", std::move(interceptor));
}
~URLRequestLoadTimingTest() override {
URLRequestFilter::GetInstance()->ClearHandlers();
}
URLRequestInterceptorWithLoadTimingInfo* interceptor() const {
return interceptor_;
}
private:
URLRequestInterceptorWithLoadTimingInfo* interceptor_;
};
// "Normal" LoadTimingInfo as returned by a job. Everything is in order, not
// reused. |connect_time_flags| is used to indicate if there should be dns
// or SSL times, and |used_proxy| is used for proxy times.
LoadTimingInfo NormalLoadTimingInfo(base::TimeTicks now,
int connect_time_flags,
bool used_proxy) {
LoadTimingInfo load_timing;
load_timing.socket_log_id = 1;
if (used_proxy) {
load_timing.proxy_resolve_start = now + base::TimeDelta::FromDays(1);
load_timing.proxy_resolve_end = now + base::TimeDelta::FromDays(2);
}
LoadTimingInfo::ConnectTiming& connect_timing = load_timing.connect_timing;
if (connect_time_flags & CONNECT_TIMING_HAS_DNS_TIMES) {
connect_timing.dns_start = now + base::TimeDelta::FromDays(3);
connect_timing.dns_end = now + base::TimeDelta::FromDays(4);
}
connect_timing.connect_start = now + base::TimeDelta::FromDays(5);
if (connect_time_flags & CONNECT_TIMING_HAS_SSL_TIMES) {
connect_timing.ssl_start = now + base::TimeDelta::FromDays(6);
connect_timing.ssl_end = now + base::TimeDelta::FromDays(7);
}
connect_timing.connect_end = now + base::TimeDelta::FromDays(8);
load_timing.send_start = now + base::TimeDelta::FromDays(9);
load_timing.send_end = now + base::TimeDelta::FromDays(10);
load_timing.receive_headers_start = now + base::TimeDelta::FromDays(11);
load_timing.receive_headers_end = now + base::TimeDelta::FromDays(12);
return load_timing;
}
// Same as above, but in the case of a reused socket.
LoadTimingInfo NormalLoadTimingInfoReused(base::TimeTicks now,
bool used_proxy) {
LoadTimingInfo load_timing;
load_timing.socket_log_id = 1;
load_timing.socket_reused = true;
if (used_proxy) {
load_timing.proxy_resolve_start = now + base::TimeDelta::FromDays(1);
load_timing.proxy_resolve_end = now + base::TimeDelta::FromDays(2);
}
load_timing.send_start = now + base::TimeDelta::FromDays(9);
load_timing.send_end = now + base::TimeDelta::FromDays(10);
load_timing.receive_headers_start = now + base::TimeDelta::FromDays(11);
load_timing.receive_headers_end = now + base::TimeDelta::FromDays(12);
return load_timing;
}
LoadTimingInfo RunURLRequestInterceptorLoadTimingTest(
const LoadTimingInfo& job_load_timing,
const URLRequestContext& context,
URLRequestInterceptorWithLoadTimingInfo* interceptor) {
interceptor->set_main_request_load_timing_info(job_load_timing);
TestDelegate d;
std::unique_ptr<URLRequest> req(
context.CreateRequest(GURL("http://test_intercept/foo"), DEFAULT_PRIORITY,
&d, TRAFFIC_ANNOTATION_FOR_TESTS));
req->Start();
d.RunUntilComplete();
LoadTimingInfo resulting_load_timing;
req->GetLoadTimingInfo(&resulting_load_timing);
// None of these should be modified by the URLRequest.
EXPECT_EQ(job_load_timing.socket_reused, resulting_load_timing.socket_reused);
EXPECT_EQ(job_load_timing.socket_log_id, resulting_load_timing.socket_log_id);
EXPECT_EQ(job_load_timing.send_start, resulting_load_timing.send_start);
EXPECT_EQ(job_load_timing.send_end, resulting_load_timing.send_end);
EXPECT_EQ(job_load_timing.receive_headers_start,
resulting_load_timing.receive_headers_start);
EXPECT_EQ(job_load_timing.receive_headers_end,
resulting_load_timing.receive_headers_end);
EXPECT_EQ(job_load_timing.push_start, resulting_load_timing.push_start);
EXPECT_EQ(job_load_timing.push_end, resulting_load_timing.push_end);
return resulting_load_timing;
}
// Basic test that the intercept + load timing tests work.
TEST_F(URLRequestLoadTimingTest, InterceptLoadTiming) {
base::TimeTicks now = base::TimeTicks::Now();
LoadTimingInfo job_load_timing =
NormalLoadTimingInfo(now, CONNECT_TIMING_HAS_DNS_TIMES, false);
LoadTimingInfo load_timing_result = RunURLRequestInterceptorLoadTimingTest(
job_load_timing, default_context(), interceptor());
// Nothing should have been changed by the URLRequest.
EXPECT_EQ(job_load_timing.proxy_resolve_start,
load_timing_result.proxy_resolve_start);
EXPECT_EQ(job_load_timing.proxy_resolve_end,
load_timing_result.proxy_resolve_end);
EXPECT_EQ(job_load_timing.connect_timing.dns_start,
load_timing_result.connect_timing.dns_start);
EXPECT_EQ(job_load_timing.connect_timing.dns_end,
load_timing_result.connect_timing.dns_end);
EXPECT_EQ(job_load_timing.connect_timing.connect_start,
load_timing_result.connect_timing.connect_start);
EXPECT_EQ(job_load_timing.connect_timing.connect_end,
load_timing_result.connect_timing.connect_end);
EXPECT_EQ(job_load_timing.connect_timing.ssl_start,
load_timing_result.connect_timing.ssl_start);
EXPECT_EQ(job_load_timing.connect_timing.ssl_end,
load_timing_result.connect_timing.ssl_end);
// Redundant sanity check.
TestLoadTimingNotReused(load_timing_result, CONNECT_TIMING_HAS_DNS_TIMES);
}
// Another basic test, with proxy and SSL times, but no DNS times.
TEST_F(URLRequestLoadTimingTest, InterceptLoadTimingProxy) {
base::TimeTicks now = base::TimeTicks::Now();
LoadTimingInfo job_load_timing =
NormalLoadTimingInfo(now, CONNECT_TIMING_HAS_SSL_TIMES, true);
LoadTimingInfo load_timing_result = RunURLRequestInterceptorLoadTimingTest(
job_load_timing, default_context(), interceptor());
// Nothing should have been changed by the URLRequest.
EXPECT_EQ(job_load_timing.proxy_resolve_start,
load_timing_result.proxy_resolve_start);
EXPECT_EQ(job_load_timing.proxy_resolve_end,
load_timing_result.proxy_resolve_end);
EXPECT_EQ(job_load_timing.connect_timing.dns_start,
load_timing_result.connect_timing.dns_start);
EXPECT_EQ(job_load_timing.connect_timing.dns_end,
load_timing_result.connect_timing.dns_end);
EXPECT_EQ(job_load_timing.connect_timing.connect_start,
load_timing_result.connect_timing.connect_start);
EXPECT_EQ(job_load_timing.connect_timing.connect_end,
load_timing_result.connect_timing.connect_end);
EXPECT_EQ(job_load_timing.connect_timing.ssl_start,
load_timing_result.connect_timing.ssl_start);
EXPECT_EQ(job_load_timing.connect_timing.ssl_end,
load_timing_result.connect_timing.ssl_end);
// Redundant sanity check.
TestLoadTimingNotReusedWithProxy(load_timing_result,
CONNECT_TIMING_HAS_SSL_TIMES);
}
// Make sure that URLRequest correctly adjusts proxy times when they're before
// |request_start|, due to already having a connected socket. This happens in
// the case of reusing a SPDY session. The connected socket is not considered
// reused in this test (May be a preconnect).
//
// To mix things up from the test above, assumes DNS times but no SSL times.
TEST_F(URLRequestLoadTimingTest, InterceptLoadTimingEarlyProxyResolution) {
base::TimeTicks now = base::TimeTicks::Now();
LoadTimingInfo job_load_timing =
NormalLoadTimingInfo(now, CONNECT_TIMING_HAS_DNS_TIMES, true);
job_load_timing.proxy_resolve_start = now - base::TimeDelta::FromDays(6);
job_load_timing.proxy_resolve_end = now - base::TimeDelta::FromDays(5);
job_load_timing.connect_timing.dns_start = now - base::TimeDelta::FromDays(4);
job_load_timing.connect_timing.dns_end = now - base::TimeDelta::FromDays(3);
job_load_timing.connect_timing.connect_start =
now - base::TimeDelta::FromDays(2);
job_load_timing.connect_timing.connect_end =
now - base::TimeDelta::FromDays(1);
LoadTimingInfo load_timing_result = RunURLRequestInterceptorLoadTimingTest(
job_load_timing, default_context(), interceptor());
// Proxy times, connect times, and DNS times should all be replaced with
// request_start.
EXPECT_EQ(load_timing_result.request_start,
load_timing_result.proxy_resolve_start);
EXPECT_EQ(load_timing_result.request_start,
load_timing_result.proxy_resolve_end);
EXPECT_EQ(load_timing_result.request_start,
load_timing_result.connect_timing.dns_start);
EXPECT_EQ(load_timing_result.request_start,
load_timing_result.connect_timing.dns_end);
EXPECT_EQ(load_timing_result.request_start,
load_timing_result.connect_timing.connect_start);
EXPECT_EQ(load_timing_result.request_start,
load_timing_result.connect_timing.connect_end);
// Other times should have been left null.
TestLoadTimingNotReusedWithProxy(load_timing_result,
CONNECT_TIMING_HAS_DNS_TIMES);
}
// Same as above, but in the reused case.
TEST_F(URLRequestLoadTimingTest,
InterceptLoadTimingEarlyProxyResolutionReused) {
base::TimeTicks now = base::TimeTicks::Now();
LoadTimingInfo job_load_timing = NormalLoadTimingInfoReused(now, true);
job_load_timing.proxy_resolve_start = now - base::TimeDelta::FromDays(4);
job_load_timing.proxy_resolve_end = now - base::TimeDelta::FromDays(3);
LoadTimingInfo load_timing_result = RunURLRequestInterceptorLoadTimingTest(
job_load_timing, default_context(), interceptor());
// Proxy times and connect times should all be replaced with request_start.
EXPECT_EQ(load_timing_result.request_start,
load_timing_result.proxy_resolve_start);
EXPECT_EQ(load_timing_result.request_start,
load_timing_result.proxy_resolve_end);
// Other times should have been left null.
TestLoadTimingReusedWithProxy(load_timing_result);
}
// Make sure that URLRequest correctly adjusts connect times when they're before
// |request_start|, due to reusing a connected socket. The connected socket is
// not considered reused in this test (May be a preconnect).
//
// To mix things up, the request has SSL times, but no DNS times.
TEST_F(URLRequestLoadTimingTest, InterceptLoadTimingEarlyConnect) {
base::TimeTicks now = base::TimeTicks::Now();
LoadTimingInfo job_load_timing =
NormalLoadTimingInfo(now, CONNECT_TIMING_HAS_SSL_TIMES, false);
job_load_timing.connect_timing.connect_start =
now - base::TimeDelta::FromDays(1);
job_load_timing.connect_timing.ssl_start = now - base::TimeDelta::FromDays(2);
job_load_timing.connect_timing.ssl_end = now - base::TimeDelta::FromDays(3);
job_load_timing.connect_timing.connect_end =
now - base::TimeDelta::FromDays(4);
LoadTimingInfo load_timing_result = RunURLRequestInterceptorLoadTimingTest(
job_load_timing, default_context(), interceptor());
// Connect times, and SSL times should be replaced with request_start.
EXPECT_EQ(load_timing_result.request_start,
load_timing_result.connect_timing.connect_start);
EXPECT_EQ(load_timing_result.request_start,
load_timing_result.connect_timing.ssl_start);
EXPECT_EQ(load_timing_result.request_start,
load_timing_result.connect_timing.ssl_end);
EXPECT_EQ(load_timing_result.request_start,
load_timing_result.connect_timing.connect_end);
// Other times should have been left null.
TestLoadTimingNotReused(load_timing_result, CONNECT_TIMING_HAS_SSL_TIMES);
}
// Make sure that URLRequest correctly adjusts connect times when they're before
// |request_start|, due to reusing a connected socket in the case that there
// are also proxy times. The connected socket is not considered reused in this
// test (May be a preconnect).
//
// In this test, there are no SSL or DNS times.
TEST_F(URLRequestLoadTimingTest, InterceptLoadTimingEarlyConnectWithProxy) {
base::TimeTicks now = base::TimeTicks::Now();
LoadTimingInfo job_load_timing =
NormalLoadTimingInfo(now, CONNECT_TIMING_HAS_CONNECT_TIMES_ONLY, true);
job_load_timing.connect_timing.connect_start =
now - base::TimeDelta::FromDays(1);
job_load_timing.connect_timing.connect_end =
now - base::TimeDelta::FromDays(2);
LoadTimingInfo load_timing_result = RunURLRequestInterceptorLoadTimingTest(
job_load_timing, default_context(), interceptor());
// Connect times should be replaced with proxy_resolve_end.
EXPECT_EQ(load_timing_result.proxy_resolve_end,
load_timing_result.connect_timing.connect_start);
EXPECT_EQ(load_timing_result.proxy_resolve_end,
load_timing_result.connect_timing.connect_end);
// Other times should have been left null.
TestLoadTimingNotReusedWithProxy(load_timing_result,
CONNECT_TIMING_HAS_CONNECT_TIMES_ONLY);
}
TEST_F(URLRequestTest, NetworkDelegateProxyError) {
MockHostResolver host_resolver;
host_resolver.rules()->AddSimulatedTimeoutFailure("*");
TestNetworkDelegate network_delegate; // Must outlive URLRequests.
TestURLRequestContextWithProxy context("myproxy:70", &network_delegate,
true /* delay_initialization */);
context.set_host_resolver(&host_resolver);
context.Init();
TestDelegate d;
std::unique_ptr<URLRequest> req(
context.CreateRequest(GURL("http://example.com"), DEFAULT_PRIORITY, &d,
TRAFFIC_ANNOTATION_FOR_TESTS));
req->set_method("GET");
req->Start();
d.RunUntilComplete();
// Check we see a failed request.
// The proxy server should be set before failure.
EXPECT_EQ(ProxyServer::FromPacString("PROXY myproxy:70"),
req->proxy_server());
EXPECT_EQ(ERR_PROXY_CONNECTION_FAILED, d.request_status());
EXPECT_THAT(req->response_info().resolve_error_info.error,
IsError(ERR_DNS_TIMED_OUT));
EXPECT_EQ(1, network_delegate.error_count());
EXPECT_THAT(network_delegate.last_error(),
IsError(ERR_PROXY_CONNECTION_FAILED));
EXPECT_EQ(1, network_delegate.completed_requests());
}
TEST_F(URLRequestTest, SkipSecureDnsDisabledByDefault) {
MockHostResolver host_resolver;
TestNetworkDelegate network_delegate; // Must outlive URLRequest.
TestURLRequestContext context(true);
context.set_network_delegate(&network_delegate);
context.set_host_resolver(&host_resolver);
context.Init();
TestDelegate d;
std::unique_ptr<URLRequest> req(
context.CreateRequest(GURL("http://example.com"), DEFAULT_PRIORITY, &d,
TRAFFIC_ANNOTATION_FOR_TESTS));
req->Start();
d.RunUntilComplete();
EXPECT_FALSE(host_resolver.last_secure_dns_mode_override().has_value());
}
TEST_F(URLRequestTest, SkipSecureDnsEnabled) {
MockHostResolver host_resolver;
TestNetworkDelegate network_delegate; // Must outlive URLRequest.
TestURLRequestContext context(true);
context.set_network_delegate(&network_delegate);
context.set_host_resolver(&host_resolver);
context.Init();
TestDelegate d;
std::unique_ptr<URLRequest> req(
context.CreateRequest(GURL("http://example.com"), DEFAULT_PRIORITY, &d,
TRAFFIC_ANNOTATION_FOR_TESTS));
req->SetDisableSecureDns(true);
req->Start();
d.RunUntilComplete();
EXPECT_EQ(net::DnsConfig::SecureDnsMode::OFF,
host_resolver.last_secure_dns_mode_override().value());
}
// Make sure that NetworkDelegate::NotifyCompleted is called if
// content is empty.
TEST_F(URLRequestTest, RequestCompletionForEmptyResponse) {
HttpTestServer test_server;
ASSERT_TRUE(test_server.Start());
TestDelegate d;
std::unique_ptr<URLRequest> req(default_context().CreateRequest(
test_server.GetURL("/nocontent"), DEFAULT_PRIORITY, &d,
TRAFFIC_ANNOTATION_FOR_TESTS));
req->Start();
d.RunUntilComplete();
EXPECT_THAT(d.request_status(), IsOk());
EXPECT_EQ(204, req->GetResponseCode());
EXPECT_EQ("", d.data_received());
EXPECT_EQ(1, default_network_delegate_.completed_requests());
}
// Make sure that SetPriority actually sets the URLRequest's priority
// correctly, both before and after start.
TEST_F(URLRequestTest, SetPriorityBasic) {
TestDelegate d;
std::unique_ptr<URLRequest> req(default_context().CreateRequest(
GURL("http://test_intercept/foo"), DEFAULT_PRIORITY, &d,
TRAFFIC_ANNOTATION_FOR_TESTS));
EXPECT_EQ(DEFAULT_PRIORITY, req->priority());
req->SetPriority(LOW);
EXPECT_EQ(LOW, req->priority());
req->Start();
EXPECT_EQ(LOW, req->priority());
req->SetPriority(MEDIUM);
EXPECT_EQ(MEDIUM, req->priority());
}
// Make sure that URLRequest calls SetPriority on a job before calling
// Start on it.
TEST_F(URLRequestTest, SetJobPriorityBeforeJobStart) {
TestDelegate d;
std::unique_ptr<URLRequest> req(default_context().CreateRequest(
GURL("http://test_intercept/foo"), DEFAULT_PRIORITY, &d,
TRAFFIC_ANNOTATION_FOR_TESTS));
EXPECT_EQ(DEFAULT_PRIORITY, req->priority());
RequestPriority job_priority;
std::unique_ptr<URLRequestJob> job(new PriorityMonitoringURLRequestJob(
req.get(), &default_network_delegate_, &job_priority));
AddTestInterceptor()->set_main_intercept_job(std::move(job));
EXPECT_EQ(DEFAULT_PRIORITY, job_priority);
req->SetPriority(LOW);
req->Start();
EXPECT_EQ(LOW, job_priority);
}
// Make sure that URLRequest passes on its priority updates to its
// job.
TEST_F(URLRequestTest, SetJobPriority) {
TestDelegate d;
std::unique_ptr<URLRequest> req(default_context().CreateRequest(
GURL("http://test_intercept/foo"), DEFAULT_PRIORITY, &d,
TRAFFIC_ANNOTATION_FOR_TESTS));
RequestPriority job_priority;
std::unique_ptr<URLRequestJob> job(new PriorityMonitoringURLRequestJob(
req.get(), &default_network_delegate_, &job_priority));
AddTestInterceptor()->set_main_intercept_job(std::move(job));
req->SetPriority(LOW);
req->Start();
EXPECT_EQ(LOW, job_priority);
req->SetPriority(MEDIUM);
EXPECT_EQ(MEDIUM, req->priority());
EXPECT_EQ(MEDIUM, job_priority);
}
// Setting the IGNORE_LIMITS load flag should be okay if the priority
// is MAXIMUM_PRIORITY.
TEST_F(URLRequestTest, PriorityIgnoreLimits) {
TestDelegate d;
std::unique_ptr<URLRequest> req(default_context().CreateRequest(
GURL("http://test_intercept/foo"), MAXIMUM_PRIORITY, &d,
TRAFFIC_ANNOTATION_FOR_TESTS));
EXPECT_EQ(MAXIMUM_PRIORITY, req->priority());
RequestPriority job_priority;
std::unique_ptr<URLRequestJob> job(new PriorityMonitoringURLRequestJob(
req.get(), &default_network_delegate_, &job_priority));
AddTestInterceptor()->set_main_intercept_job(std::move(job));
req->SetLoadFlags(LOAD_IGNORE_LIMITS);
EXPECT_EQ(MAXIMUM_PRIORITY, req->priority());
req->SetPriority(MAXIMUM_PRIORITY);
EXPECT_EQ(MAXIMUM_PRIORITY, req->priority());
req->Start();
EXPECT_EQ(MAXIMUM_PRIORITY, req->priority());
EXPECT_EQ(MAXIMUM_PRIORITY, job_priority);
}
// This test verifies that URLRequest::Delegate's OnConnected() callback is
// never called if the request fails before connecting to a remote endpoint.
TEST_F(URLRequestTest, NotifyDelegateConnectedSkippedOnEarlyFailure) {
TestDelegate delegate;
// The request will never connect to anything because the URL is invalid.
auto request =
default_context().CreateRequest(GURL("invalid url"), DEFAULT_PRIORITY,
&delegate, TRAFFIC_ANNOTATION_FOR_TESTS);
request->Start();
delegate.RunUntilComplete();
EXPECT_THAT(delegate.transports(), IsEmpty());
}
// This test verifies that URLRequest::Delegate's OnConnected() callback
// is called once for simple redirect-less requests.
TEST_F(URLRequestTest, NotifyDelegateConnectedOnce) {
HttpTestServer test_server;
ASSERT_TRUE(test_server.Start());
TestDelegate delegate;
auto request = default_context().CreateRequest(test_server.GetURL("/echo"),
DEFAULT_PRIORITY, &delegate,
TRAFFIC_ANNOTATION_FOR_TESTS);
request->Start();
delegate.RunUntilComplete();
TransportInfo expected_transport;
expected_transport.endpoint =
IPEndPoint(IPAddress::IPv4Localhost(), test_server.port());
EXPECT_THAT(delegate.transports(), ElementsAre(expected_transport));
}
// This test verifies that URLRequest::Delegate's OnConnected() callback is
// called after each redirect.
TEST_F(URLRequestTest, NotifyDelegateConnectedOnEachRedirect) {
HttpTestServer test_server;
ASSERT_TRUE(test_server.Start());
TestDelegate delegate;
// Fetch a page that redirects us once.
GURL url = test_server.GetURL("/server-redirect?" +
test_server.GetURL("/echo").spec());
auto request = default_context().CreateRequest(
url, DEFAULT_PRIORITY, &delegate, TRAFFIC_ANNOTATION_FOR_TESTS);
request->Start();
delegate.RunUntilRedirect();
TransportInfo expected_transport;
expected_transport.endpoint =
IPEndPoint(IPAddress::IPv4Localhost(), test_server.port());
EXPECT_THAT(delegate.transports(), ElementsAre(expected_transport));
request->FollowDeferredRedirect(/*removed_headers=*/{},
/*modified_headers=*/{});
delegate.RunUntilComplete();
EXPECT_THAT(delegate.transports(),
ElementsAre(expected_transport, expected_transport));
}
// This test verifies that when the URLRequest Delegate returns an error from
// OnBeforeSendHeaders(), the entire request fails with that error.
TEST_F(URLRequestTest, NotifyDelegateConnectedReturnError) {
HttpTestServer test_server;
ASSERT_TRUE(test_server.Start());
TestDelegate delegate;
delegate.set_on_connected_result(ERR_NOT_IMPLEMENTED);
auto request = default_context().CreateRequest(test_server.GetURL("/echo"),
DEFAULT_PRIORITY, &delegate,
TRAFFIC_ANNOTATION_FOR_TESTS);
request->Start();
delegate.RunUntilComplete();
TransportInfo expected_transport;
expected_transport.endpoint =
IPEndPoint(IPAddress::IPv4Localhost(), test_server.port());
EXPECT_THAT(delegate.transports(), ElementsAre(expected_transport));
EXPECT_TRUE(delegate.request_failed());
EXPECT_THAT(delegate.request_status(), IsError(ERR_NOT_IMPLEMENTED));
}
TEST_F(URLRequestTest, DelayedCookieCallback) {
HttpTestServer test_server;
ASSERT_TRUE(test_server.Start());
TestURLRequestContext context;
std::unique_ptr<DelayedCookieMonster> delayed_cm(new DelayedCookieMonster());
context.set_cookie_store(delayed_cm.get());
// Set up a cookie.
{
TestNetworkDelegate network_delegate;
context.set_network_delegate(&network_delegate);
TestDelegate d;
std::unique_ptr<URLRequest> req(context.CreateFirstPartyRequest(
test_server.GetURL("/set-cookie?CookieToNotSend=1"), DEFAULT_PRIORITY,
&d, TRAFFIC_ANNOTATION_FOR_TESTS));
req->Start();
d.RunUntilComplete();
EXPECT_EQ(0, network_delegate.blocked_get_cookies_count());
EXPECT_EQ(0, network_delegate.blocked_set_cookie_count());
EXPECT_EQ(1, network_delegate.set_cookie_count());
}
// Verify that the cookie is set.
{
TestNetworkDelegate network_delegate;
context.set_network_delegate(&network_delegate);
TestDelegate d;
std::unique_ptr<URLRequest> req(context.CreateFirstPartyRequest(
test_server.GetURL("/echoheader?Cookie"), DEFAULT_PRIORITY, &d,
TRAFFIC_ANNOTATION_FOR_TESTS));
req->Start();
d.RunUntilComplete();
EXPECT_TRUE(d.data_received().find("CookieToNotSend=1") !=
std::string::npos);
EXPECT_EQ(0, network_delegate.blocked_get_cookies_count());
EXPECT_EQ(0, network_delegate.blocked_set_cookie_count());
}
}
class FilteringTestNetworkDelegate : public TestNetworkDelegate {
public:
FilteringTestNetworkDelegate()
: set_cookie_called_count_(0),
blocked_set_cookie_count_(0),
block_get_cookies_(false),
get_cookie_called_count_(0),
blocked_get_cookie_count_(0) {}
~FilteringTestNetworkDelegate() override = default;
bool OnCanSetCookie(const URLRequest& request,
const net::CanonicalCookie& cookie,
CookieOptions* options,
bool allowed_from_caller) override {
// Filter out cookies with the same name as |cookie_name_filter_| and
// combine with |allowed_from_caller|.
bool allowed =
allowed_from_caller && !(cookie.Name() == cookie_name_filter_);
++set_cookie_called_count_;
if (!allowed)
++blocked_set_cookie_count_;
return TestNetworkDelegate::OnCanSetCookie(request, cookie, options,
allowed);
}
void SetCookieFilter(std::string filter) {
cookie_name_filter_ = std::move(filter);
}
int set_cookie_called_count() { return set_cookie_called_count_; }
int blocked_set_cookie_count() { return blocked_set_cookie_count_; }
void ResetSetCookieCalledCount() { set_cookie_called_count_ = 0; }
void ResetBlockedSetCookieCount() { blocked_set_cookie_count_ = 0; }
bool OnCanGetCookies(const URLRequest& request,
bool allowed_from_caller) override {
// Filter out cookies if |block_get_cookies_| is set and
// combine with |allowed_from_caller|.
bool allowed = allowed_from_caller && !block_get_cookies_;
++get_cookie_called_count_;
if (!allowed)
++blocked_get_cookie_count_;
return TestNetworkDelegate::OnCanGetCookies(request, allowed);
}
void set_block_get_cookies() { block_get_cookies_ = true; }
void unset_block_get_cookies() { block_get_cookies_ = false; }
int get_cookie_called_count() const { return get_cookie_called_count_; }
int blocked_get_cookie_count() const { return blocked_get_cookie_count_; }
void ResetGetCookieCalledCount() { get_cookie_called_count_ = 0; }
void ResetBlockedGetCookieCount() { blocked_get_cookie_count_ = 0; }
private:
std::string cookie_name_filter_;
int set_cookie_called_count_;
int blocked_set_cookie_count_;
bool block_get_cookies_;
int get_cookie_called_count_;
int blocked_get_cookie_count_;
DISALLOW_COPY_AND_ASSIGN(FilteringTestNetworkDelegate);
};
TEST_F(URLRequestTest, DelayedCookieCallbackAsync) {
HttpTestServer test_server;
ASSERT_TRUE(test_server.Start());
TestURLRequestContext async_context;
std::unique_ptr<DelayedCookieMonster> delayed_cm =
std::make_unique<DelayedCookieMonster>();
async_context.set_cookie_store(delayed_cm.get());
FilteringTestNetworkDelegate async_filter_network_delegate;
async_filter_network_delegate.SetCookieFilter("CookieBlockedOnCanGetCookie");
async_context.set_network_delegate(&async_filter_network_delegate);
TestDelegate async_delegate;
TestURLRequestContext sync_context;
std::unique_ptr<CookieMonster> cm =
std::make_unique<CookieMonster>(nullptr, nullptr);
sync_context.set_cookie_store(cm.get());
FilteringTestNetworkDelegate sync_filter_network_delegate;
sync_filter_network_delegate.SetCookieFilter("CookieBlockedOnCanGetCookie");
sync_context.set_network_delegate(&sync_filter_network_delegate);
TestDelegate sync_delegate;
// Add a secure cookie so we can try to set an insecure cookie and have
// SetCanonicalCookie fail.
GURL::Replacements replace_scheme;
replace_scheme.SetSchemeStr("https");
GURL url = test_server.base_url().ReplaceComponents(replace_scheme);
auto cookie1 = CanonicalCookie::Create(url, "AlreadySetCookie=1;Secure",
base::Time::Now(),
base::nullopt /* server_time */);
delayed_cm->SetCanonicalCookieAsync(std::move(cookie1), url,
net::CookieOptions::MakeAllInclusive(),
CookieStore::SetCookiesCallback());
auto cookie2 = CanonicalCookie::Create(url, "AlreadySetCookie=1;Secure",
base::Time::Now(),
base::nullopt /* server_time */);
cm->SetCanonicalCookieAsync(std::move(cookie2), url,
net::CookieOptions::MakeAllInclusive(),
CookieStore::SetCookiesCallback());
std::vector<std::string> cookie_lines(
{// Fails in SetCanonicalCookie for trying to set a secure cookie
// on an insecure host.
"CookieNotSet=1;Secure",
// Fail in FilteringTestNetworkDelegate::CanGetCookie.
"CookieBlockedOnCanGetCookie=1",
// Fails in SetCanonicalCookie for trying to overwrite a secure cookie
// with an insecure cookie.
"AlreadySetCookie=1",
// Succeeds and added cookie to store. Delayed (which makes the callback
// run asynchronously) in DelayedCookieMonster.
"CookieSet=1"});
for (auto first_cookie_line : cookie_lines) {
for (auto second_cookie_line : cookie_lines) {
// Run with the delayed cookie monster.
std::unique_ptr<URLRequest> request =
async_context.CreateFirstPartyRequest(
test_server.GetURL("/set-cookie?" + first_cookie_line + "&" +
second_cookie_line),
DEFAULT_PRIORITY, &async_delegate, TRAFFIC_ANNOTATION_FOR_TESTS);
request->Start();
async_delegate.RunUntilComplete();
EXPECT_THAT(async_delegate.request_status(), IsOk());
// Run with the regular cookie monster.
request = sync_context.CreateFirstPartyRequest(
test_server.GetURL("/set-cookie?" + first_cookie_line + "&" +
second_cookie_line),
DEFAULT_PRIORITY, &sync_delegate, TRAFFIC_ANNOTATION_FOR_TESTS);
request->Start();
sync_delegate.RunUntilComplete();
EXPECT_THAT(sync_delegate.request_status(), IsOk());
int expected_set_cookie_count = 0;
int expected_blocked_cookie_count = 0;
// 2 calls to the delegate's OnCanSetCookie method are expected, even if
// the cookies don't end up getting set.
expected_set_cookie_count += 2;
if (first_cookie_line == "CookieBlockedOnCanGetCookie=1")
++expected_blocked_cookie_count;
if (second_cookie_line == "CookieBlockedOnCanGetCookie=1")
++expected_blocked_cookie_count;
EXPECT_EQ(expected_set_cookie_count,
async_filter_network_delegate.set_cookie_called_count());
EXPECT_EQ(expected_blocked_cookie_count,
async_filter_network_delegate.blocked_set_cookie_count());
EXPECT_EQ(expected_set_cookie_count,
sync_filter_network_delegate.set_cookie_called_count());
EXPECT_EQ(expected_blocked_cookie_count,
sync_filter_network_delegate.blocked_set_cookie_count());
async_filter_network_delegate.ResetSetCookieCalledCount();
async_filter_network_delegate.ResetBlockedSetCookieCount();
sync_filter_network_delegate.ResetSetCookieCalledCount();
sync_filter_network_delegate.ResetBlockedSetCookieCount();
}
}
}
TEST_F(URLRequestTest, DoNotSendCookies) {
HttpTestServer test_server;
ASSERT_TRUE(test_server.Start());
// Set up a cookie.
{
TestNetworkDelegate network_delegate;
default_context().set_network_delegate(&network_delegate);
TestDelegate d;
std::unique_ptr<URLRequest> req(default_context().CreateFirstPartyRequest(
test_server.GetURL("/set-cookie?CookieToNotSend=1"), DEFAULT_PRIORITY,
&d, TRAFFIC_ANNOTATION_FOR_TESTS));
req->Start();
d.RunUntilComplete();
EXPECT_EQ(0, network_delegate.blocked_get_cookies_count());
EXPECT_EQ(0, network_delegate.blocked_set_cookie_count());
}
// Verify that the cookie is set.
{
TestNetworkDelegate network_delegate;
default_context().set_network_delegate(&network_delegate);
TestDelegate d;
std::unique_ptr<URLRequest> req(default_context().CreateFirstPartyRequest(
test_server.GetURL("/echoheader?Cookie"), DEFAULT_PRIORITY, &d,
TRAFFIC_ANNOTATION_FOR_TESTS));
req->Start();
d.RunUntilComplete();
EXPECT_TRUE(d.data_received().find("CookieToNotSend=1") !=
std::string::npos);
EXPECT_EQ(0, network_delegate.blocked_get_cookies_count());
EXPECT_EQ(0, network_delegate.blocked_set_cookie_count());
}
// Verify that the cookie isn't sent when LOAD_DO_NOT_SEND_COOKIES is set.
{
TestNetworkDelegate network_delegate;
default_context().set_network_delegate(&network_delegate);
TestDelegate d;
std::unique_ptr<URLRequest> req(default_context().CreateFirstPartyRequest(
test_server.GetURL("/echoheader?Cookie"), DEFAULT_PRIORITY, &d,
TRAFFIC_ANNOTATION_FOR_TESTS));
req->SetLoadFlags(LOAD_DO_NOT_SEND_COOKIES);
req->Start();
d.RunUntilComplete();
EXPECT_TRUE(d.data_received().find("Cookie: CookieToNotSend=1") ==
std::string::npos);
// LOAD_DO_NOT_SEND_COOKIES does not trigger OnGetCookies.
EXPECT_EQ(0, network_delegate.blocked_get_cookies_count());
EXPECT_EQ(0, network_delegate.blocked_set_cookie_count());
}
}
TEST_F(URLRequestTest, DoNotSaveCookies) {
HttpTestServer test_server;
ASSERT_TRUE(test_server.Start());
// Set up a cookie.
{
TestNetworkDelegate network_delegate;
default_context().set_network_delegate(&network_delegate);
TestDelegate d;
std::unique_ptr<URLRequest> req(default_context().CreateFirstPartyRequest(
test_server.GetURL("/set-cookie?CookieToNotUpdate=2"), DEFAULT_PRIORITY,
&d, TRAFFIC_ANNOTATION_FOR_TESTS));
req->Start();
d.RunUntilComplete();
EXPECT_EQ(0, network_delegate.blocked_get_cookies_count());
EXPECT_EQ(0, network_delegate.blocked_set_cookie_count());
EXPECT_EQ(1, network_delegate.set_cookie_count());
}
// Try to set-up another cookie and update the previous cookie.
{
TestNetworkDelegate network_delegate;
default_context().set_network_delegate(&network_delegate);
TestDelegate d;
std::unique_ptr<URLRequest> req(default_context().CreateFirstPartyRequest(
test_server.GetURL("/set-cookie?CookieToNotSave=1&CookieToNotUpdate=1"),
DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS));
req->SetLoadFlags(LOAD_DO_NOT_SAVE_COOKIES);
req->Start();
d.RunUntilComplete();
// LOAD_DO_NOT_SAVE_COOKIES does not trigger OnSetCookie.
EXPECT_EQ(0, network_delegate.blocked_get_cookies_count());
EXPECT_EQ(0, network_delegate.blocked_set_cookie_count());
EXPECT_EQ(0, network_delegate.set_cookie_count());
}
// Verify the cookies weren't saved or updated.
{
TestNetworkDelegate network_delegate;
default_context().set_network_delegate(&network_delegate);
TestDelegate d;
std::unique_ptr<URLRequest> req(default_context().CreateFirstPartyRequest(
test_server.GetURL("/echoheader?Cookie"), DEFAULT_PRIORITY, &d,
TRAFFIC_ANNOTATION_FOR_TESTS));
req->Start();
d.RunUntilComplete();
EXPECT_TRUE(d.data_received().find("CookieToNotSave=1") ==
std::string::npos);
EXPECT_TRUE(d.data_received().find("CookieToNotUpdate=2") !=
std::string::npos);
EXPECT_EQ(0, network_delegate.blocked_get_cookies_count());
EXPECT_EQ(0, network_delegate.blocked_set_cookie_count());
EXPECT_EQ(0, network_delegate.set_cookie_count());
}
}
TEST_F(URLRequestTest, DoNotSendCookies_ViaPolicy) {
HttpTestServer test_server;
ASSERT_TRUE(test_server.Start());
// Set up a cookie.
{
TestNetworkDelegate network_delegate;
default_context().set_network_delegate(&network_delegate);
TestDelegate d;
std::unique_ptr<URLRequest> req(default_context().CreateFirstPartyRequest(
test_server.GetURL("/set-cookie?CookieToNotSend=1"), DEFAULT_PRIORITY,
&d, TRAFFIC_ANNOTATION_FOR_TESTS));
req->Start();
d.RunUntilComplete();
EXPECT_EQ(0, network_delegate.blocked_get_cookies_count());
EXPECT_EQ(0, network_delegate.blocked_set_cookie_count());
}
// Verify that the cookie is set.
{
TestNetworkDelegate network_delegate;
default_context().set_network_delegate(&network_delegate);
TestDelegate d;
std::unique_ptr<URLRequest> req(default_context().CreateFirstPartyRequest(
test_server.GetURL("/echoheader?Cookie"), DEFAULT_PRIORITY, &d,
TRAFFIC_ANNOTATION_FOR_TESTS));
req->Start();
d.RunUntilComplete();
EXPECT_TRUE(d.data_received().find("CookieToNotSend=1") !=
std::string::npos);
EXPECT_EQ(0, network_delegate.blocked_get_cookies_count());
EXPECT_EQ(0, network_delegate.blocked_set_cookie_count());
auto entries = net_log_.GetEntries();
for (const auto& entry : entries) {
EXPECT_NE(entry.type,
NetLogEventType::COOKIE_GET_BLOCKED_BY_NETWORK_DELEGATE);
}
}
// Verify that the cookie isn't sent.
{
TestNetworkDelegate network_delegate;
default_context().set_network_delegate(&network_delegate);
TestDelegate d;
network_delegate.set_cookie_options(TestNetworkDelegate::NO_GET_COOKIES);
std::unique_ptr<URLRequest> req(default_context().CreateFirstPartyRequest(
test_server.GetURL("/echoheader?Cookie"), DEFAULT_PRIORITY, &d,
TRAFFIC_ANNOTATION_FOR_TESTS));
req->Start();
d.RunUntilComplete();
EXPECT_TRUE(d.data_received().find("Cookie: CookieToNotSend=1") ==
std::string::npos);
EXPECT_EQ(1, network_delegate.blocked_get_cookies_count());
EXPECT_EQ(0, network_delegate.blocked_set_cookie_count());
auto entries = net_log_.GetEntries();
ExpectLogContainsSomewhereAfter(
entries, 0, NetLogEventType::COOKIE_GET_BLOCKED_BY_NETWORK_DELEGATE,
NetLogEventPhase::NONE);
}
}
// TODO(crbug.com/564656) This test is flaky on iOS.
#if defined(OS_IOS)
#define MAYBE_DoNotSaveCookies_ViaPolicy FLAKY_DoNotSaveCookies_ViaPolicy
#else
#define MAYBE_DoNotSaveCookies_ViaPolicy DoNotSaveCookies_ViaPolicy
#endif
TEST_F(URLRequestTest, DoNotSaveCookies_ViaPolicy) {
HttpTestServer test_server;
ASSERT_TRUE(test_server.Start());
// Set up a cookie.
{
TestNetworkDelegate network_delegate;
default_context().set_network_delegate(&network_delegate);
TestDelegate d;
std::unique_ptr<URLRequest> req(default_context().CreateFirstPartyRequest(
test_server.GetURL("/set-cookie?CookieToNotUpdate=2"), DEFAULT_PRIORITY,
&d, TRAFFIC_ANNOTATION_FOR_TESTS));
req->Start();
d.RunUntilComplete();
EXPECT_EQ(0, network_delegate.blocked_get_cookies_count());
EXPECT_EQ(0, network_delegate.blocked_set_cookie_count());
auto entries = net_log_.GetEntries();
for (const auto& entry : entries) {
EXPECT_NE(entry.type,
NetLogEventType::COOKIE_SET_BLOCKED_BY_NETWORK_DELEGATE);
}
}
// Try to set-up another cookie and update the previous cookie.
{
TestNetworkDelegate network_delegate;
default_context().set_network_delegate(&network_delegate);
TestDelegate d;
network_delegate.set_cookie_options(TestNetworkDelegate::NO_SET_COOKIE);
std::unique_ptr<URLRequest> req(default_context().CreateFirstPartyRequest(
test_server.GetURL("/set-cookie?CookieToNotSave=1&CookieToNotUpdate=1"),
DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS));
req->Start();
d.RunUntilComplete();
EXPECT_EQ(0, network_delegate.blocked_get_cookies_count());
EXPECT_EQ(2, network_delegate.blocked_set_cookie_count());
auto entries = net_log_.GetEntries();
ExpectLogContainsSomewhereAfter(
entries, 0, NetLogEventType::COOKIE_SET_BLOCKED_BY_NETWORK_DELEGATE,
NetLogEventPhase::NONE);
}
// Verify the cookies weren't saved or updated.
{
TestNetworkDelegate network_delegate;
default_context().set_network_delegate(&network_delegate);
TestDelegate d;
std::unique_ptr<URLRequest> req(default_context().CreateFirstPartyRequest(
test_server.GetURL("/echoheader?Cookie"), DEFAULT_PRIORITY, &d,
TRAFFIC_ANNOTATION_FOR_TESTS));
req->Start();
d.RunUntilComplete();
EXPECT_TRUE(d.data_received().find("CookieToNotSave=1") ==
std::string::npos);
EXPECT_TRUE(d.data_received().find("CookieToNotUpdate=2") !=
std::string::npos);
EXPECT_EQ(0, network_delegate.blocked_get_cookies_count());
EXPECT_EQ(0, network_delegate.blocked_set_cookie_count());
}
}
TEST_F(URLRequestTest, DoNotSaveEmptyCookies) {
HttpTestServer test_server;
ASSERT_TRUE(test_server.Start());
// Set up an empty cookie.
{
TestNetworkDelegate network_delegate;
default_context().set_network_delegate(&network_delegate);
TestDelegate d;
std::unique_ptr<URLRequest> req(default_context().CreateFirstPartyRequest(
test_server.GetURL("/set-cookie"), DEFAULT_PRIORITY, &d,
TRAFFIC_ANNOTATION_FOR_TESTS));
req->Start();
d.RunUntilComplete();
EXPECT_EQ(0, network_delegate.blocked_get_cookies_count());
EXPECT_EQ(0, network_delegate.blocked_set_cookie_count());
EXPECT_EQ(0, network_delegate.set_cookie_count());
}
}
TEST_F(URLRequestTest, DoNotSendCookies_ViaPolicy_Async) {
HttpTestServer test_server;
ASSERT_TRUE(test_server.Start());
// Set up a cookie.
{
TestNetworkDelegate network_delegate;
default_context().set_network_delegate(&network_delegate);
TestDelegate d;
std::unique_ptr<URLRequest> req(default_context().CreateFirstPartyRequest(
test_server.GetURL("/set-cookie?CookieToNotSend=1"), DEFAULT_PRIORITY,
&d, TRAFFIC_ANNOTATION_FOR_TESTS));
req->Start();
d.RunUntilComplete();
EXPECT_EQ(0, network_delegate.blocked_get_cookies_count());
EXPECT_EQ(0, network_delegate.blocked_set_cookie_count());
}
// Verify that the cookie is set.
{
TestNetworkDelegate network_delegate;
default_context().set_network_delegate(&network_delegate);
TestDelegate d;
std::unique_ptr<URLRequest> req(default_context().CreateFirstPartyRequest(
test_server.GetURL("/echoheader?Cookie"), DEFAULT_PRIORITY, &d,
TRAFFIC_ANNOTATION_FOR_TESTS));
req->Start();
d.RunUntilComplete();
EXPECT_TRUE(d.data_received().find("CookieToNotSend=1") !=
std::string::npos);
EXPECT_EQ(0, network_delegate.blocked_get_cookies_count());
EXPECT_EQ(0, network_delegate.blocked_set_cookie_count());
}
// Verify that the cookie isn't sent.
{
TestNetworkDelegate network_delegate;
default_context().set_network_delegate(&network_delegate);
TestDelegate d;
network_delegate.set_cookie_options(TestNetworkDelegate::NO_GET_COOKIES);
std::unique_ptr<URLRequest> req(default_context().CreateFirstPartyRequest(
test_server.GetURL("/echoheader?Cookie"), DEFAULT_PRIORITY, &d,
TRAFFIC_ANNOTATION_FOR_TESTS));
req->Start();
d.RunUntilComplete();
EXPECT_TRUE(d.data_received().find("Cookie: CookieToNotSend=1") ==
std::string::npos);
EXPECT_EQ(1, network_delegate.blocked_get_cookies_count());
EXPECT_EQ(0, network_delegate.blocked_set_cookie_count());
}
}
TEST_F(URLRequestTest, DoNotSaveCookies_ViaPolicy_Async) {
HttpTestServer test_server;
ASSERT_TRUE(test_server.Start());
// Set up a cookie.
{
TestNetworkDelegate network_delegate;
default_context().set_network_delegate(&network_delegate);
TestDelegate d;
std::unique_ptr<URLRequest> req(default_context().CreateFirstPartyRequest(
test_server.GetURL("/set-cookie?CookieToNotUpdate=2"), DEFAULT_PRIORITY,
&d, TRAFFIC_ANNOTATION_FOR_TESTS));
req->Start();
d.RunUntilComplete();
EXPECT_EQ(0, network_delegate.blocked_get_cookies_count());
EXPECT_EQ(0, network_delegate.blocked_set_cookie_count());
}
// Try to set-up another cookie and update the previous cookie.
{
TestNetworkDelegate network_delegate;
default_context().set_network_delegate(&network_delegate);
TestDelegate d;
network_delegate.set_cookie_options(TestNetworkDelegate::NO_SET_COOKIE);
std::unique_ptr<URLRequest> req(default_context().CreateFirstPartyRequest(
test_server.GetURL("/set-cookie?CookieToNotSave=1&CookieToNotUpdate=1"),
DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS));
req->Start();
d.RunUntilComplete();
EXPECT_EQ(0, network_delegate.blocked_get_cookies_count());
EXPECT_EQ(2, network_delegate.blocked_set_cookie_count());
}
// Verify the cookies weren't saved or updated.
{
TestNetworkDelegate network_delegate;
default_context().set_network_delegate(&network_delegate);
TestDelegate d;
std::unique_ptr<URLRequest> req(default_context().CreateFirstPartyRequest(
test_server.GetURL("/echoheader?Cookie"), DEFAULT_PRIORITY, &d,
TRAFFIC_ANNOTATION_FOR_TESTS));
req->Start();
d.RunUntilComplete();
EXPECT_TRUE(d.data_received().find("CookieToNotSave=1") ==
std::string::npos);
EXPECT_TRUE(d.data_received().find("CookieToNotUpdate=2") !=
std::string::npos);
EXPECT_EQ(0, network_delegate.blocked_get_cookies_count());
EXPECT_EQ(0, network_delegate.blocked_set_cookie_count());
}
}
TEST_F(URLRequestTest, SameSiteCookies) {
HttpTestServer test_server;
ASSERT_TRUE(test_server.Start());
TestNetworkDelegate network_delegate;
default_context().set_network_delegate(&network_delegate);
const std::string kHost = "example.test";
const std::string kSubHost = "subdomain.example.test";
const std::string kCrossHost = "cross-origin.test";
// Set up two 'SameSite' cookies on 'example.test'
{
TestDelegate d;
std::unique_ptr<URLRequest> req(default_context().CreateFirstPartyRequest(
test_server.GetURL(kHost,
"/set-cookie?StrictSameSiteCookie=1;SameSite=Strict&"
"LaxSameSiteCookie=1;SameSite=Lax"),
DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS));
req->set_site_for_cookies(
SiteForCookies::FromUrl(test_server.GetURL(kHost, "/")));
req->set_initiator(url::Origin::Create(test_server.GetURL(kHost, "/")));
req->Start();
d.RunUntilComplete();
EXPECT_EQ(0, network_delegate.blocked_get_cookies_count());
EXPECT_EQ(0, network_delegate.blocked_set_cookie_count());
EXPECT_EQ(2, network_delegate.set_cookie_count());
}
// Verify that both cookies are sent for same-site requests.
{
TestDelegate d;
std::unique_ptr<URLRequest> req(default_context().CreateRequest(
test_server.GetURL(kHost, "/echoheader?Cookie"), DEFAULT_PRIORITY, &d,
TRAFFIC_ANNOTATION_FOR_TESTS));
req->set_site_for_cookies(
SiteForCookies::FromUrl(test_server.GetURL(kHost, "/")));
req->set_initiator(url::Origin::Create(test_server.GetURL(kHost, "/")));
req->Start();
d.RunUntilComplete();
EXPECT_NE(std::string::npos,
d.data_received().find("StrictSameSiteCookie=1"));
EXPECT_NE(std::string::npos, d.data_received().find("LaxSameSiteCookie=1"));
EXPECT_EQ(0, network_delegate.blocked_get_cookies_count());
EXPECT_EQ(0, network_delegate.blocked_set_cookie_count());
}
// Verify that both cookies are sent when the request has no initiator (can
// happen for main frame browser-initiated navigations).
{
TestDelegate d;
std::unique_ptr<URLRequest> req(default_context().CreateRequest(
test_server.GetURL(kHost, "/echoheader?Cookie"), DEFAULT_PRIORITY, &d,
TRAFFIC_ANNOTATION_FOR_TESTS));
req->set_site_for_cookies(
SiteForCookies::FromUrl(test_server.GetURL(kHost, "/")));
req->Start();
d.RunUntilComplete();
EXPECT_NE(std::string::npos,
d.data_received().find("StrictSameSiteCookie=1"));
EXPECT_NE(std::string::npos, d.data_received().find("LaxSameSiteCookie=1"));
EXPECT_EQ(0, network_delegate.blocked_get_cookies_count());
EXPECT_EQ(0, network_delegate.blocked_set_cookie_count());
}
// Verify that both cookies are sent for same-registrable-domain requests.
{
TestDelegate d;
std::unique_ptr<URLRequest> req(default_context().CreateRequest(
test_server.GetURL(kHost, "/echoheader?Cookie"), DEFAULT_PRIORITY, &d,
TRAFFIC_ANNOTATION_FOR_TESTS));
req->set_site_for_cookies(
SiteForCookies::FromUrl(test_server.GetURL(kSubHost, "/")));
req->set_initiator(url::Origin::Create(test_server.GetURL(kSubHost, "/")));
req->Start();
d.RunUntilComplete();
EXPECT_NE(std::string::npos,
d.data_received().find("StrictSameSiteCookie=1"));
EXPECT_NE(std::string::npos, d.data_received().find("LaxSameSiteCookie=1"));
EXPECT_EQ(0, network_delegate.blocked_get_cookies_count());
EXPECT_EQ(0, network_delegate.blocked_set_cookie_count());
}
// Verify that neither cookie is not sent for cross-site requests.
{
TestDelegate d;
std::unique_ptr<URLRequest> req(default_context().CreateRequest(
test_server.GetURL(kHost, "/echoheader?Cookie"), DEFAULT_PRIORITY, &d,
TRAFFIC_ANNOTATION_FOR_TESTS));
req->set_site_for_cookies(
SiteForCookies::FromUrl(test_server.GetURL(kCrossHost, "/")));
req->set_initiator(
url::Origin::Create(test_server.GetURL(kCrossHost, "/")));
req->Start();
d.RunUntilComplete();
EXPECT_EQ(std::string::npos,
d.data_received().find("StrictSameSiteCookie=1"));
EXPECT_EQ(std::string::npos, d.data_received().find("LaxSameSiteCookie=1"));
EXPECT_EQ(0, network_delegate.blocked_get_cookies_count());
EXPECT_EQ(0, network_delegate.blocked_set_cookie_count());
}
// Verify that the lax cookie is sent for cross-site initiators when the
// method is "safe".
{
TestDelegate d;
std::unique_ptr<URLRequest> req(default_context().CreateRequest(
test_server.GetURL(kHost, "/echoheader?Cookie"), DEFAULT_PRIORITY, &d,
TRAFFIC_ANNOTATION_FOR_TESTS));
req->set_site_for_cookies(
SiteForCookies::FromUrl(test_server.GetURL(kHost, "/")));
req->set_initiator(
url::Origin::Create(test_server.GetURL(kCrossHost, "/")));
req->set_method("GET");
req->Start();
d.RunUntilComplete();
EXPECT_EQ(std::string::npos,
d.data_received().find("StrictSameSiteCookie=1"));
EXPECT_NE(std::string::npos, d.data_received().find("LaxSameSiteCookie=1"));
EXPECT_EQ(0, network_delegate.blocked_get_cookies_count());
EXPECT_EQ(0, network_delegate.blocked_set_cookie_count());
}
// Verify that neither cookie is sent for cross-site initiators when the
// method is unsafe (e.g. POST).
{
TestDelegate d;
std::unique_ptr<URLRequest> req(default_context().CreateRequest(
test_server.GetURL(kHost, "/echoheader?Cookie"), DEFAULT_PRIORITY, &d,
TRAFFIC_ANNOTATION_FOR_TESTS));
req->set_site_for_cookies(
SiteForCookies::FromUrl(test_server.GetURL(kHost, "/")));
req->set_initiator(
url::Origin::Create(test_server.GetURL(kCrossHost, "/")));
req->set_method("POST");
req->Start();
d.RunUntilComplete();
EXPECT_EQ(std::string::npos,
d.data_received().find("StrictSameSiteCookie=1"));
EXPECT_EQ(std::string::npos, d.data_received().find("LaxSameSiteCookie=1"));
EXPECT_EQ(0, network_delegate.blocked_get_cookies_count());
EXPECT_EQ(0, network_delegate.blocked_set_cookie_count());
}
}
TEST_F(URLRequestTest, SettingSameSiteCookies) {
HttpTestServer test_server;
ASSERT_TRUE(test_server.Start());
TestNetworkDelegate network_delegate;
default_context().set_network_delegate(&network_delegate);
const std::string kHost = "example.test";
const std::string kSubHost = "subdomain.example.test";
const std::string kCrossHost = "cross-origin.test";
int expected_cookies = 0;
{
TestDelegate d;
std::unique_ptr<URLRequest> req(default_context().CreateRequest(
test_server.GetURL(kHost,
"/set-cookie?Strict1=1;SameSite=Strict&"
"Lax1=1;SameSite=Lax"),
DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS));
req->set_site_for_cookies(
SiteForCookies::FromUrl(test_server.GetURL(kHost, "/")));
req->set_initiator(url::Origin::Create(test_server.GetURL(kHost, "/")));
// 'SameSite' cookies are settable from strict same-site contexts
// (same-origin site_for_cookies, same-origin initiator), so this request
// should result in two cookies being set.
expected_cookies += 2;
req->Start();
d.RunUntilComplete();
EXPECT_EQ(expected_cookies,
static_cast<int>(GetAllCookies(&default_context()).size()));
EXPECT_EQ(expected_cookies, network_delegate.set_cookie_count());
}
{
TestDelegate d;
std::unique_ptr<URLRequest> req(default_context().CreateRequest(
test_server.GetURL(kHost,
"/set-cookie?Strict2=1;SameSite=Strict&"
"Lax2=1;SameSite=Lax"),
DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS));
req->set_site_for_cookies(
SiteForCookies::FromUrl(test_server.GetURL(kHost, "/")));
req->set_initiator(
url::Origin::Create(test_server.GetURL(kCrossHost, "/")));
// 'SameSite' cookies are settable from lax same-site contexts (same-origin
// site_for_cookies, cross-site initiator), so this request should result in
// two cookies being set.
expected_cookies += 2;
req->Start();
d.RunUntilComplete();
EXPECT_EQ(expected_cookies,
static_cast<int>(GetAllCookies(&default_context()).size()));
EXPECT_EQ(expected_cookies, network_delegate.set_cookie_count());
}
{
TestDelegate d;
std::unique_ptr<URLRequest> req(default_context().CreateRequest(
test_server.GetURL(kHost,
"/set-cookie?Strict3=1;SameSite=Strict&"
"Lax3=1;SameSite=Lax"),
DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS));
req->set_site_for_cookies(
SiteForCookies::FromUrl(test_server.GetURL(kSubHost, "/")));
req->set_initiator(
url::Origin::Create(test_server.GetURL(kCrossHost, "/")));
// 'SameSite' cookies are settable from lax same-site contexts (same-site
// site_for_cookies, cross-site initiator), so this request should result in
// two cookies being set.
expected_cookies += 2;
req->Start();
d.RunUntilComplete();
EXPECT_EQ(expected_cookies,
static_cast<int>(GetAllCookies(&default_context()).size()));
EXPECT_EQ(expected_cookies, network_delegate.set_cookie_count());
}
{
TestDelegate d;
std::unique_ptr<URLRequest> req(default_context().CreateRequest(
test_server.GetURL(kHost,
"/set-cookie?Strict4=1;SameSite=Strict&"
"Lax4=1;SameSite=Lax"),
DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS));
req->set_site_for_cookies(
SiteForCookies::FromUrl(test_server.GetURL(kSubHost, "/")));
// 'SameSite' cookies are settable from strict same-site contexts (same-site
// site_for_cookies, no initiator), so this request should result in two
// cookies being set.
expected_cookies += 2;
req->Start();
d.RunUntilComplete();
EXPECT_EQ(expected_cookies,
static_cast<int>(GetAllCookies(&default_context()).size()));
EXPECT_EQ(expected_cookies, network_delegate.set_cookie_count());
}
{
TestDelegate d;
std::unique_ptr<URLRequest> req(default_context().CreateRequest(
test_server.GetURL(kHost,
"/set-cookie?Strict5=1;SameSite=Strict&"
"Lax5=1;SameSite=Lax"),
DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS));
req->set_site_for_cookies(
SiteForCookies::FromUrl(test_server.GetURL(kCrossHost, "/")));
req->set_initiator(
url::Origin::Create(test_server.GetURL(kCrossHost, "/")));
// 'SameSite' cookies are not settable from cross-site contexts, so this
// should not result in any new cookies being set.
expected_cookies += 0;
req->Start();
d.RunUntilComplete();
// This counts the number of cookies actually set.
EXPECT_EQ(expected_cookies,
static_cast<int>(GetAllCookies(&default_context()).size()));
// This counts the number of successful calls to CanSetCookie() when
// attempting to set a cookie. The two cookies above were created and
// attempted to be set, and were not rejected by the NetworkDelegate, so the
// count here is 2 more than the number of cookies actually set.
EXPECT_EQ(expected_cookies + 2, network_delegate.set_cookie_count());
}
}
// Tests special chrome:// scheme that is supposed to always attach SameSite
// cookies if the requested site is secure.
TEST_F(URLRequestTest, SameSiteCookiesSpecialScheme) {
url::ScopedSchemeRegistryForTests scoped_registry;
url::AddStandardScheme("chrome", url::SchemeType::SCHEME_WITH_HOST);
EmbeddedTestServer https_test_server(EmbeddedTestServer::TYPE_HTTPS);
RegisterDefaultHandlers(&https_test_server);
ASSERT_TRUE(https_test_server.Start());
EmbeddedTestServer http_test_server(EmbeddedTestServer::TYPE_HTTP);
RegisterDefaultHandlers(&http_test_server);
// Ensure they are on different ports.
ASSERT_TRUE(http_test_server.Start(https_test_server.port() + 1));
// Both hostnames should be 127.0.0.1 (so that we can use the same set of
// cookies on both, for convenience).
ASSERT_EQ(https_test_server.host_port_pair().host(),
http_test_server.host_port_pair().host());
// Set up special schemes
auto cad = std::make_unique<TestCookieAccessDelegate>();
cad->SetIgnoreSameSiteRestrictionsScheme("chrome", true);
CookieMonster cm(nullptr, nullptr);
cm.SetCookieAccessDelegate(std::move(cad));
TestURLRequestContext context(true);
context.set_cookie_store(&cm);
context.Init();
// SameSite cookies are not set for 'chrome' scheme if requested origin is not
// secure.
{
TestDelegate d;
std::unique_ptr<URLRequest> req(context.CreateRequest(
http_test_server.GetURL(
"/set-cookie?StrictSameSiteCookie=1;SameSite=Strict&"
"LaxSameSiteCookie=1;SameSite=Lax"),
DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS));
req->set_site_for_cookies(
SiteForCookies::FromUrl(GURL("chrome://whatever/")));
req->Start();
d.RunUntilComplete();
EXPECT_EQ(0u, GetAllCookies(&context).size());
}
// But they are set for 'chrome' scheme if the requested origin is secure.
{
TestDelegate d;
std::unique_ptr<URLRequest> req(context.CreateRequest(
https_test_server.GetURL(
"/set-cookie?StrictSameSiteCookie=1;SameSite=Strict&"
"LaxSameSiteCookie=1;SameSite=Lax"),
DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS));
req->set_site_for_cookies(
SiteForCookies::FromUrl(GURL("chrome://whatever/")));
req->Start();
d.RunUntilComplete();
CookieList cookies = GetAllCookies(&context);
EXPECT_EQ(2u, cookies.size());
}
// Verify that they are both sent when the site_for_cookies scheme is
// 'chrome' and the requested origin is secure.
{
TestDelegate d;
std::unique_ptr<URLRequest> req(context.CreateRequest(
https_test_server.GetURL("/echoheader?Cookie"), DEFAULT_PRIORITY, &d,
TRAFFIC_ANNOTATION_FOR_TESTS));
req->set_site_for_cookies(
SiteForCookies::FromUrl(GURL("chrome://whatever/")));
req->Start();
d.RunUntilComplete();
EXPECT_NE(std::string::npos,
d.data_received().find("StrictSameSiteCookie=1"));
EXPECT_NE(std::string::npos, d.data_received().find("LaxSameSiteCookie=1"));
}
// Verify that they are not sent when the site_for_cookies scheme is
// 'chrome' and the requested origin is not secure.
{
TestDelegate d;
std::unique_ptr<URLRequest> req(context.CreateRequest(
http_test_server.GetURL("/echoheader?Cookie"), DEFAULT_PRIORITY, &d,
TRAFFIC_ANNOTATION_FOR_TESTS));
req->set_site_for_cookies(
SiteForCookies::FromUrl(GURL("chrome://whatever/")));
req->Start();
d.RunUntilComplete();
EXPECT_EQ(std::string::npos,
d.data_received().find("StrictSameSiteCookie"));
EXPECT_EQ(std::string::npos, d.data_received().find("LaxSameSiteCookie"));
}
}
// Tests that __Secure- cookies can't be set on non-secure origins.
TEST_F(URLRequestTest, SecureCookiePrefixOnNonsecureOrigin) {
EmbeddedTestServer http_server;
RegisterDefaultHandlers(&http_server);
EmbeddedTestServer https_server(EmbeddedTestServer::TYPE_HTTPS);
RegisterDefaultHandlers(&https_server);
ASSERT_TRUE(http_server.Start());
ASSERT_TRUE(https_server.Start());
TestNetworkDelegate network_delegate;
TestURLRequestContext context(true);
context.set_network_delegate(&network_delegate);
context.Init();
// Try to set a Secure __Secure- cookie.
{
TestDelegate d;
std::unique_ptr<URLRequest> req(context.CreateFirstPartyRequest(
http_server.GetURL("/set-cookie?__Secure-nonsecure-origin=1;Secure"),
DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS));
req->Start();
d.RunUntilComplete();
EXPECT_EQ(0, network_delegate.blocked_get_cookies_count());
EXPECT_EQ(0, network_delegate.blocked_set_cookie_count());
}
// Verify that the cookie is not set.
{
TestDelegate d;
std::unique_ptr<URLRequest> req(context.CreateFirstPartyRequest(
https_server.GetURL("/echoheader?Cookie"), DEFAULT_PRIORITY, &d,
TRAFFIC_ANNOTATION_FOR_TESTS));
req->Start();
d.RunUntilComplete();
EXPECT_EQ(d.data_received().find("__Secure-nonsecure-origin=1"),
std::string::npos);
EXPECT_EQ(0, network_delegate.blocked_get_cookies_count());
EXPECT_EQ(0, network_delegate.blocked_set_cookie_count());
}
}
TEST_F(URLRequestTest, SecureCookiePrefixNonsecure) {
EmbeddedTestServer https_server(EmbeddedTestServer::TYPE_HTTPS);
RegisterDefaultHandlers(&https_server);
ASSERT_TRUE(https_server.Start());
TestNetworkDelegate network_delegate;
TestURLRequestContext context(true);
context.set_network_delegate(&network_delegate);
context.Init();
// Try to set a non-Secure __Secure- cookie.
{
TestDelegate d;
std::unique_ptr<URLRequest> req(context.CreateFirstPartyRequest(
https_server.GetURL("/set-cookie?__Secure-foo=1"), DEFAULT_PRIORITY, &d,
TRAFFIC_ANNOTATION_FOR_TESTS));
req->Start();
d.RunUntilComplete();
EXPECT_EQ(0, network_delegate.set_cookie_count());
EXPECT_EQ(0, network_delegate.blocked_get_cookies_count());
EXPECT_EQ(0, network_delegate.blocked_set_cookie_count());
}
// Verify that the cookie is not set.
{
TestDelegate d;
std::unique_ptr<URLRequest> req(context.CreateFirstPartyRequest(
https_server.GetURL("/echoheader?Cookie"), DEFAULT_PRIORITY, &d,
TRAFFIC_ANNOTATION_FOR_TESTS));
req->Start();
d.RunUntilComplete();
EXPECT_EQ(d.data_received().find("__Secure-foo=1"), std::string::npos);
EXPECT_EQ(0, network_delegate.blocked_get_cookies_count());
EXPECT_EQ(0, network_delegate.blocked_set_cookie_count());
}
}
TEST_F(URLRequestTest, SecureCookiePrefixSecure) {
EmbeddedTestServer https_server(EmbeddedTestServer::TYPE_HTTPS);
RegisterDefaultHandlers(&https_server);
ASSERT_TRUE(https_server.Start());
TestNetworkDelegate network_delegate;
TestURLRequestContext context(true);
context.set_network_delegate(&network_delegate);
context.Init();
// Try to set a Secure __Secure- cookie.
{
TestDelegate d;
std::unique_ptr<URLRequest> req(context.CreateFirstPartyRequest(
https_server.GetURL("/set-cookie?__Secure-bar=1;Secure"),
DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS));
req->Start();
d.RunUntilComplete();
EXPECT_EQ(0, network_delegate.blocked_get_cookies_count());
EXPECT_EQ(0, network_delegate.blocked_set_cookie_count());
}
// Verify that the cookie is set.
{
TestDelegate d;
std::unique_ptr<URLRequest> req(context.CreateFirstPartyRequest(
https_server.GetURL("/echoheader?Cookie"), DEFAULT_PRIORITY, &d,
TRAFFIC_ANNOTATION_FOR_TESTS));
req->Start();
d.RunUntilComplete();
EXPECT_NE(d.data_received().find("__Secure-bar=1"), std::string::npos);
EXPECT_EQ(0, network_delegate.blocked_get_cookies_count());
EXPECT_EQ(0, network_delegate.blocked_set_cookie_count());
}
}
// Tests that secure cookies can't be set on non-secure origins if strict secure
// cookies are enabled.
TEST_F(URLRequestTest, StrictSecureCookiesOnNonsecureOrigin) {
EmbeddedTestServer http_server;
RegisterDefaultHandlers(&http_server);
EmbeddedTestServer https_server(EmbeddedTestServer::TYPE_HTTPS);
RegisterDefaultHandlers(&https_server);
ASSERT_TRUE(http_server.Start());
ASSERT_TRUE(https_server.Start());
TestNetworkDelegate network_delegate;
TestURLRequestContext context(true);
context.set_network_delegate(&network_delegate);
context.Init();
// Try to set a Secure cookie, with experimental features enabled.
{
TestDelegate d;
std::unique_ptr<URLRequest> req(context.CreateFirstPartyRequest(
http_server.GetURL("/set-cookie?nonsecure-origin=1;Secure"),
DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS));
req->Start();
d.RunUntilComplete();
EXPECT_EQ(0, network_delegate.blocked_get_cookies_count());
EXPECT_EQ(0, network_delegate.blocked_set_cookie_count());
}
// Verify that the cookie is not set.
{
TestDelegate d;
std::unique_ptr<URLRequest> req(context.CreateFirstPartyRequest(
https_server.GetURL("/echoheader?Cookie"), DEFAULT_PRIORITY, &d,
TRAFFIC_ANNOTATION_FOR_TESTS));
req->Start();
d.RunUntilComplete();
EXPECT_EQ(d.data_received().find("nonsecure-origin=1"), std::string::npos);
EXPECT_EQ(0, network_delegate.blocked_get_cookies_count());
EXPECT_EQ(0, network_delegate.blocked_set_cookie_count());
}
}
// FixedDateNetworkDelegate swaps out the server's HTTP Date response header
// value for the |fixed_date| argument given to the constructor.
class FixedDateNetworkDelegate : public TestNetworkDelegate {
public:
explicit FixedDateNetworkDelegate(const std::string& fixed_date)
: fixed_date_(fixed_date) {}
~FixedDateNetworkDelegate() override = default;
// NetworkDelegate implementation
int OnHeadersReceived(
URLRequest* request,
CompletionOnceCallback callback,
const HttpResponseHeaders* original_response_headers,
scoped_refptr<HttpResponseHeaders>* override_response_headers,
const IPEndPoint& endpoint,
base::Optional<GURL>* preserve_fragment_on_redirect_url) override;
private:
std::string fixed_date_;
DISALLOW_COPY_AND_ASSIGN(FixedDateNetworkDelegate);
};
int FixedDateNetworkDelegate::OnHeadersReceived(
URLRequest* request,
CompletionOnceCallback callback,
const HttpResponseHeaders* original_response_headers,
scoped_refptr<HttpResponseHeaders>* override_response_headers,
const IPEndPoint& endpoint,
base::Optional<GURL>* preserve_fragment_on_redirect_url) {
HttpResponseHeaders* new_response_headers =
new HttpResponseHeaders(original_response_headers->raw_headers());
new_response_headers->SetHeader("Date", fixed_date_);
*override_response_headers = new_response_headers;
return TestNetworkDelegate::OnHeadersReceived(
request, std::move(callback), original_response_headers,
override_response_headers, endpoint, preserve_fragment_on_redirect_url);
}
// Test that cookie expiration times are adjusted for server/client clock
// skew and that we handle incorrect timezone specifier "UTC" in HTTP Date
// headers by defaulting to GMT. (crbug.com/135131)
TEST_F(URLRequestTest, AcceptClockSkewCookieWithWrongDateTimezone) {
HttpTestServer test_server;
ASSERT_TRUE(test_server.Start());
// Set up an expired cookie.
{
TestNetworkDelegate network_delegate;
default_context().set_network_delegate(&network_delegate);
TestDelegate d;
std::unique_ptr<URLRequest> req(default_context().CreateFirstPartyRequest(
test_server.GetURL(
"/set-cookie?StillGood=1;expires=Mon,18-Apr-1977,22:50:13,GMT"),
DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS));
req->Start();
d.RunUntilComplete();
}
// Verify that the cookie is not set.
{
TestNetworkDelegate network_delegate;
default_context().set_network_delegate(&network_delegate);
TestDelegate d;
std::unique_ptr<URLRequest> req(default_context().CreateFirstPartyRequest(
test_server.GetURL("/echoheader?Cookie"), DEFAULT_PRIORITY, &d,
TRAFFIC_ANNOTATION_FOR_TESTS));
req->Start();
d.RunUntilComplete();
EXPECT_TRUE(d.data_received().find("StillGood=1") == std::string::npos);
}
// Set up a cookie with clock skew and "UTC" HTTP Date timezone specifier.
{
FixedDateNetworkDelegate network_delegate("18-Apr-1977 22:49:13 UTC");
default_context().set_network_delegate(&network_delegate);
TestDelegate d;
std::unique_ptr<URLRequest> req(default_context().CreateFirstPartyRequest(
test_server.GetURL(
"/set-cookie?StillGood=1;expires=Mon,18-Apr-1977,22:50:13,GMT"),
DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS));
req->Start();
d.RunUntilComplete();
}
// Verify that the cookie is set.
{
TestNetworkDelegate network_delegate;
default_context().set_network_delegate(&network_delegate);
TestDelegate d;
std::unique_ptr<URLRequest> req(default_context().CreateFirstPartyRequest(
test_server.GetURL("/echoheader?Cookie"), DEFAULT_PRIORITY, &d,
TRAFFIC_ANNOTATION_FOR_TESTS));
req->Start();
d.RunUntilComplete();
EXPECT_TRUE(d.data_received().find("StillGood=1") != std::string::npos);
}
}
// Check that it is impossible to change the referrer in the extra headers of
// an URLRequest.
TEST_F(URLRequestTest, DoNotOverrideReferrer) {
HttpTestServer test_server;
ASSERT_TRUE(test_server.Start());
// If extra headers contain referer and the request contains a referer,
// only the latter shall be respected.
{
TestDelegate d;
std::unique_ptr<URLRequest> req(default_context().CreateRequest(
test_server.GetURL("/echoheader?Referer"), DEFAULT_PRIORITY, &d,
TRAFFIC_ANNOTATION_FOR_TESTS));
req->SetReferrer("http://foo.com/");
HttpRequestHeaders headers;
headers.SetHeader(HttpRequestHeaders::kReferer, "http://bar.com/");
req->SetExtraRequestHeaders(headers);
req->Start();
d.RunUntilComplete();
EXPECT_EQ("http://foo.com/", d.data_received());
}
// If extra headers contain a referer but the request does not, no referer
// shall be sent in the header.
{
TestDelegate d;
std::unique_ptr<URLRequest> req(default_context().CreateRequest(
test_server.GetURL("/echoheader?Referer"), DEFAULT_PRIORITY, &d,
TRAFFIC_ANNOTATION_FOR_TESTS));
HttpRequestHeaders headers;
headers.SetHeader(HttpRequestHeaders::kReferer, "http://bar.com/");
req->SetExtraRequestHeaders(headers);
req->SetLoadFlags(LOAD_VALIDATE_CACHE);
req->Start();
d.RunUntilComplete();
EXPECT_EQ("None", d.data_received());
}
}
class URLRequestTestHTTP : public URLRequestTest {
public:
const url::Origin origin1_;
const url::Origin origin2_;
const IsolationInfo isolation_info1_;
const IsolationInfo isolation_info2_;
URLRequestTestHTTP()
: origin1_(url::Origin::Create(GURL("https://foo.test/"))),
origin2_(url::Origin::Create(GURL("https://bar.test/"))),
isolation_info1_(IsolationInfo::CreateForInternalRequest(origin1_)),
isolation_info2_(IsolationInfo::CreateForInternalRequest(origin2_)),
test_server_(base::FilePath(kTestFilePath)) {}
protected:
// ProtocolHandler for the scheme that's unsafe to redirect to.
class NET_EXPORT UnsafeRedirectProtocolHandler
: public URLRequestJobFactory::ProtocolHandler {
public:
UnsafeRedirectProtocolHandler() = default;
~UnsafeRedirectProtocolHandler() override = default;
// URLRequestJobFactory::ProtocolHandler implementation:
URLRequestJob* MaybeCreateJob(
URLRequest* request,
NetworkDelegate* network_delegate) const override {
NOTREACHED();
return nullptr;
}
bool IsSafeRedirectTarget(const GURL& location) const override {
return false;
}
private:
DISALLOW_COPY_AND_ASSIGN(UnsafeRedirectProtocolHandler);
};
// URLRequestTest interface:
void SetUpFactory() override {
// Add FTP support to the default URLRequestContext.
job_factory_impl_->SetProtocolHandler(
"unsafe", std::make_unique<UnsafeRedirectProtocolHandler>());
}
// Requests |redirect_url|, which must return a HTTP 3xx redirect.
// |request_method| is the method to use for the initial request.
// |redirect_method| is the method that is expected to be used for the second
// request, after redirection.
// If |include_data| is true, data is uploaded with the request. The
// response body is expected to match it exactly, if and only if
// |request_method| == |redirect_method|.
void HTTPRedirectMethodTest(const GURL& redirect_url,
const std::string& request_method,
const std::string& redirect_method,
bool include_data) {
static const char kData[] = "hello world";
TestDelegate d;
std::unique_ptr<URLRequest> req(default_context().CreateFirstPartyRequest(
redirect_url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS));
req->set_method(request_method);
if (include_data) {
req->set_upload(CreateSimpleUploadData(kData));
HttpRequestHeaders headers;
headers.SetHeader(HttpRequestHeaders::kContentLength,
base::NumberToString(base::size(kData) - 1));
headers.SetHeader(HttpRequestHeaders::kContentType, "text/plain");
req->SetExtraRequestHeaders(headers);
}
req->Start();
d.RunUntilComplete();
EXPECT_EQ(redirect_method, req->method());
EXPECT_EQ(OK, d.request_status());
if (include_data) {
if (request_method == redirect_method) {
EXPECT_TRUE(req->extra_request_headers().HasHeader(
HttpRequestHeaders::kContentLength));
EXPECT_TRUE(req->extra_request_headers().HasHeader(
HttpRequestHeaders::kContentType));
EXPECT_EQ(kData, d.data_received());
} else {
EXPECT_FALSE(req->extra_request_headers().HasHeader(
HttpRequestHeaders::kContentLength));
EXPECT_FALSE(req->extra_request_headers().HasHeader(
HttpRequestHeaders::kContentType));
EXPECT_NE(kData, d.data_received());
}
}
if (HasFailure())
LOG(WARNING) << "Request method was: " << request_method;
}
// Requests |redirect_url|, which must return a HTTP 3xx redirect. It's also
// used as the initial origin.
// |request_method| is the method to use for the initial request.
// |redirect_method| is the method that is expected to be used for the second
// request, after redirection.
// |expected_origin_value| is the expected value for the Origin header after
// redirection. If empty, expects that there will be no Origin header.
void HTTPRedirectOriginHeaderTest(const GURL& redirect_url,
const std::string& request_method,
const std::string& redirect_method,
const std::string& expected_origin_value) {
TestDelegate d;
std::unique_ptr<URLRequest> req(default_context().CreateFirstPartyRequest(
redirect_url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS));
req->set_method(request_method);
req->SetExtraRequestHeaderByName(HttpRequestHeaders::kOrigin,
redirect_url.GetOrigin().spec(), false);
req->Start();
d.RunUntilComplete();
EXPECT_EQ(redirect_method, req->method());
// Note that there is no check for request success here because, for
// purposes of testing, the request very well may fail. For example, if the
// test redirects to an HTTPS server from an HTTP origin, thus it is cross
// origin, there is not an HTTPS server in this unit test framework, so the
// request would fail. However, that's fine, as long as the request headers
// are in order and pass the checks below.
if (expected_origin_value.empty()) {
EXPECT_FALSE(
req->extra_request_headers().HasHeader(HttpRequestHeaders::kOrigin));
} else {
std::string origin_header;
EXPECT_TRUE(req->extra_request_headers().GetHeader(
HttpRequestHeaders::kOrigin, &origin_header));
EXPECT_EQ(expected_origin_value, origin_header);
}
}
void HTTPUploadDataOperationTest(const std::string& method) {