blob: e96c6019352cff309c9f3438ff55fe0300b6d81e [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 <memory>
#include <utility>
// This must be before Windows headers
#include "build/build_config.h"
#if defined(OS_WIN)
#include <windows.h>
#include <objbase.h>
#include <shlobj.h>
#endif
#include <stdint.h>
#include <algorithm>
#include <limits>
#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/macros.h"
#include "base/memory/ptr_util.h"
#include "base/memory/weak_ptr.h"
#include "base/message_loop/message_loop.h"
#include "base/path_service.h"
#include "base/power_monitor/power_monitor.h"
#include "base/power_monitor/power_monitor_source.h"
#include "base/run_loop.h"
#include "base/single_thread_task_runner.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/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 "net/base/chunked_upload_data_stream.h"
#include "net/base/directory_listing.h"
#include "net/base/elements_upload_data_stream.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/request_priority.h"
#include "net/base/test_completion_callback.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/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_net/nss_ocsp.h"
#include "net/cookies/cookie_monster.h"
#include "net/cookies/cookie_store_test_helpers.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_impl.h"
#include "net/http/http_util.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_entry.h"
#include "net/log/test_net_log_util.h"
#include "net/net_features.h"
#include "net/nqe/external_estimate_provider.h"
#include "net/proxy/proxy_server.h"
#include "net/proxy/proxy_service.h"
#include "net/quic/chromium/mock_crypto_client_stream_factory.h"
#include "net/quic/chromium/quic_server_info.h"
#include "net/socket/socket_test_util.h"
#include "net/socket/ssl_client_socket.h"
#include "net/ssl/channel_id_service.h"
#include "net/ssl/default_channel_id_store.h"
#include "net/ssl/ssl_connection_status_flags.h"
#include "net/ssl/ssl_server_config.h"
#include "net/ssl/token_binding.h"
#include "net/test/cert_test_util.h"
#include "net/test/embedded_test_server/embedded_test_server.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/url_request/url_request_failed_job.h"
#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
#include "net/url_request/data_protocol_handler.h"
#include "net/url_request/network_error_logging_delegate.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_intercepting_job_factory.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"
#if !BUILDFLAG(DISABLE_FILE_SUPPORT)
#include "net/base/filename_util.h"
#include "net/url_request/file_protocol_handler.h"
#include "net/url_request/url_request_file_dir_job.h"
#endif
#if !BUILDFLAG(DISABLE_FTP_SUPPORT) && !defined(OS_ANDROID)
#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"
#include "base/win/scoped_comptr.h"
#endif
#if BUILDFLAG(ENABLE_REPORTING)
#include "net/reporting/reporting_service.h"
#endif // BUILDFLAG(ENABLE_REPORTING)
using net::test::IsError;
using net::test::IsOk;
using base::ASCIIToUTF16;
using base::Time;
using std::string;
namespace net {
namespace {
namespace test0 {
#include "net/http/transport_security_state_static_unittest0.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)
// 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_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_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_end);
}
#if !BUILDFLAG(DISABLE_FILE_SUPPORT)
// Tests load timing information in the case of a cache hit, when no cache
// validation request was sent over the wire.
base::StringPiece TestNetResourceProvider(int key) {
return "header";
}
void FillBuffer(char* buffer, size_t len) {
static bool called = false;
if (!called) {
called = true;
int seed = static_cast<int>(Time::Now().ToInternalValue());
srand(seed);
}
for (size_t i = 0; i < len; i++) {
buffer[i] = static_cast<char>(rand());
if (!buffer[i])
buffer[i] = 'g';
}
}
#endif
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_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)
// 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_end.is_null());
}
#endif
// Test power monitor source that can simulate entering suspend mode. Can't use
// the one in base/ because it insists on bringing its own MessageLoop.
class TestPowerMonitorSource : public base::PowerMonitorSource {
public:
TestPowerMonitorSource() {}
~TestPowerMonitorSource() override {}
void Suspend() { ProcessPowerEvent(SUSPEND_EVENT); }
void Resume() { ProcessPowerEvent(RESUME_EVENT); }
bool IsOnBatteryPowerImpl() override { return false; }
private:
DISALLOW_COPY_AND_ASSIGN(TestPowerMonitorSource);
};
// 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) {
// -1 means unknown. 0 means no encryption.
EXPECT_GT(ssl_info.security_bits, 0);
// 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);
}
void CheckFullRequestHeaders(const HttpRequestHeaders& headers,
const GURL& host_url) {
std::string sent_value;
EXPECT_TRUE(headers.GetHeader("Host", &sent_value));
EXPECT_EQ(GetHostAndOptionalPort(host_url), sent_value);
EXPECT_TRUE(headers.GetHeader("Connection", &sent_value));
EXPECT_EQ("keep-alive", sent_value);
}
// 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,
ON_AUTH_REQUIRED = 1 << 3
};
// 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.
USER_NOTIFY, // User is notified by a provided callback of the
// blocking, and synchronously returns instructions
// for handling it.
};
using NotificationCallback =
base::Callback<Error(const CompletionCallback&, const URLRequest*)>;
using NotificationAuthCallback =
base::Callback<NetworkDelegate::AuthRequiredResponse(const AuthCallback&,
const URLRequest*)>;
// Creates a delegate which does not block at all.
explicit BlockingNetworkDelegate(BlockMode block_mode);
// 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);
void DoAuthCallback(NetworkDelegate::AuthRequiredResponse 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;
}
// If |auth_retval| == AUTH_REQUIRED_RESPONSE_SET_AUTH, then
// |auth_credentials_| will be passed with the response.
void set_auth_retval(AuthRequiredResponse auth_retval) {
ASSERT_NE(USER_CALLBACK, block_mode_);
ASSERT_NE(AUTH_REQUIRED_RESPONSE_IO_PENDING, auth_retval);
auth_retval_ = auth_retval;
}
void set_auth_credentials(const AuthCredentials& auth_credentials) {
auth_credentials_ = auth_credentials;
}
void set_redirect_url(const GURL& url) {
redirect_url_ = url;
}
void set_block_on(int block_on) {
block_on_ = block_on;
}
// Only valid if |block_mode_| == USER_NOTIFY
void set_notification_callback(
const NotificationCallback& notification_callback) {
notification_callback_ = notification_callback;
}
void set_notification_auth_callback(
const NotificationAuthCallback& notification_auth_callback) {
notification_auth_callback_ = notification_auth_callback;
}
// 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 RunCallback(int response, const CompletionCallback& callback);
void RunAuthCallback(AuthRequiredResponse response,
const AuthCallback& callback);
// TestNetworkDelegate implementation.
int OnBeforeURLRequest(URLRequest* request,
const CompletionCallback& callback,
GURL* new_url) override;
int OnBeforeStartTransaction(URLRequest* request,
const CompletionCallback& callback,
HttpRequestHeaders* headers) override;
int OnHeadersReceived(
URLRequest* request,
const CompletionCallback& callback,
const HttpResponseHeaders* original_response_headers,
scoped_refptr<HttpResponseHeaders>* override_response_headers,
GURL* allowed_unsafe_redirect_url) override;
NetworkDelegate::AuthRequiredResponse OnAuthRequired(
URLRequest* request,
const AuthChallengeInfo& auth_info,
const AuthCallback& callback,
AuthCredentials* credentials) 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,
const URLRequest* request,
const CompletionCallback& 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_; // To be returned in non-auth stages.
AuthRequiredResponse auth_retval_;
GURL redirect_url_; // Used if non-empty during OnBeforeURLRequest.
int block_on_; // Bit mask: in which stages to block.
// |auth_credentials_| will be copied to |*target_auth_credential_| on
// callback.
AuthCredentials auth_credentials_;
AuthCredentials* target_auth_credentials_;
// 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.
CompletionCallback callback_;
AuthCallback auth_callback_;
// Callback to request user instructions for blocking.
NotificationCallback notification_callback_;
NotificationAuthCallback notification_auth_callback_;
base::WeakPtrFactory<BlockingNetworkDelegate> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(BlockingNetworkDelegate);
};
BlockingNetworkDelegate::BlockingNetworkDelegate(BlockMode block_mode)
: block_mode_(block_mode),
retval_(OK),
auth_retval_(AUTH_REQUIRED_RESPONSE_NO_ACTION),
block_on_(0),
target_auth_credentials_(NULL),
stage_blocked_for_callback_(NOT_BLOCKED),
weak_factory_(this) {
}
void BlockingNetworkDelegate::DoCallback(int response) {
ASSERT_EQ(USER_CALLBACK, block_mode_);
ASSERT_NE(NOT_BLOCKED, stage_blocked_for_callback_);
ASSERT_NE(ON_AUTH_REQUIRED, stage_blocked_for_callback_);
CompletionCallback callback = callback_;
Reset();
RunCallback(response, callback);
}
void BlockingNetworkDelegate::DoAuthCallback(
NetworkDelegate::AuthRequiredResponse response) {
ASSERT_EQ(USER_CALLBACK, block_mode_);
ASSERT_EQ(ON_AUTH_REQUIRED, stage_blocked_for_callback_);
AuthCallback auth_callback = auth_callback_;
Reset();
RunAuthCallback(response, auth_callback);
}
void BlockingNetworkDelegate::RunCallback(int response,
const CompletionCallback& callback) {
callback.Run(response);
}
void BlockingNetworkDelegate::RunAuthCallback(AuthRequiredResponse response,
const AuthCallback& callback) {
if (auth_retval_ == AUTH_REQUIRED_RESPONSE_SET_AUTH) {
ASSERT_TRUE(target_auth_credentials_ != NULL);
*target_auth_credentials_ = auth_credentials_;
}
callback.Run(response);
}
int BlockingNetworkDelegate::OnBeforeURLRequest(
URLRequest* request,
const CompletionCallback& callback,
GURL* new_url) {
if (redirect_url_ == request->url())
return OK; // We've already seen this request and redirected elsewhere.
TestNetworkDelegate::OnBeforeURLRequest(request, callback, new_url);
if (!redirect_url_.is_empty())
*new_url = redirect_url_;
return MaybeBlockStage(ON_BEFORE_URL_REQUEST, request, callback);
}
int BlockingNetworkDelegate::OnBeforeStartTransaction(
URLRequest* request,
const CompletionCallback& callback,
HttpRequestHeaders* headers) {
TestNetworkDelegate::OnBeforeStartTransaction(request, callback, headers);
return MaybeBlockStage(ON_BEFORE_SEND_HEADERS, request, callback);
}
int BlockingNetworkDelegate::OnHeadersReceived(
URLRequest* request,
const CompletionCallback& callback,
const HttpResponseHeaders* original_response_headers,
scoped_refptr<HttpResponseHeaders>* override_response_headers,
GURL* allowed_unsafe_redirect_url) {
TestNetworkDelegate::OnHeadersReceived(request,
callback,
original_response_headers,
override_response_headers,
allowed_unsafe_redirect_url);
return MaybeBlockStage(ON_HEADERS_RECEIVED, request, callback);
}
NetworkDelegate::AuthRequiredResponse BlockingNetworkDelegate::OnAuthRequired(
URLRequest* request,
const AuthChallengeInfo& auth_info,
const AuthCallback& callback,
AuthCredentials* credentials) {
TestNetworkDelegate::OnAuthRequired(request, auth_info, callback,
credentials);
// Check that the user has provided callback for the previous blocked stage.
EXPECT_EQ(NOT_BLOCKED, stage_blocked_for_callback_);
if ((block_on_ & ON_AUTH_REQUIRED) == 0) {
return AUTH_REQUIRED_RESPONSE_NO_ACTION;
}
target_auth_credentials_ = credentials;
switch (block_mode_) {
case SYNCHRONOUS:
if (auth_retval_ == AUTH_REQUIRED_RESPONSE_SET_AUTH)
*target_auth_credentials_ = auth_credentials_;
return auth_retval_;
case AUTO_CALLBACK:
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::Bind(&BlockingNetworkDelegate::RunAuthCallback,
weak_factory_.GetWeakPtr(), auth_retval_, callback));
return AUTH_REQUIRED_RESPONSE_IO_PENDING;
case USER_CALLBACK:
auth_callback_ = callback;
stage_blocked_for_callback_ = ON_AUTH_REQUIRED;
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::MessageLoop::QuitWhenIdleClosure());
return AUTH_REQUIRED_RESPONSE_IO_PENDING;
case USER_NOTIFY:
// If the callback returns ERR_IO_PENDING, the user has accepted
// responsibility for running the callback in the future.
return notification_auth_callback_.Run(callback, request);
}
NOTREACHED();
return AUTH_REQUIRED_RESPONSE_NO_ACTION; // Dummy value.
}
void BlockingNetworkDelegate::Reset() {
EXPECT_NE(NOT_BLOCKED, stage_blocked_for_callback_);
stage_blocked_for_callback_ = NOT_BLOCKED;
callback_.Reset();
auth_callback_.Reset();
}
int BlockingNetworkDelegate::MaybeBlockStage(
BlockingNetworkDelegate::Stage stage,
const URLRequest* request,
const CompletionCallback& 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::Bind(&BlockingNetworkDelegate::RunCallback,
weak_factory_.GetWeakPtr(), retval_, callback));
return ERR_IO_PENDING;
case USER_CALLBACK:
callback_ = callback;
stage_blocked_for_callback_ = stage;
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::MessageLoop::QuitWhenIdleClosure());
return ERR_IO_PENDING;
case USER_NOTIFY:
// If the callback returns ERR_IO_PENDING, the user has accepted
// responsibility for running the callback in the future.
return notification_callback_.Run(callback, request);
}
NOTREACHED();
return 0;
}
class TestURLRequestContextWithProxy : public TestURLRequestContext {
public:
// Does not own |delegate|.
TestURLRequestContextWithProxy(const std::string& proxy,
NetworkDelegate* delegate)
: TestURLRequestContext(true) {
context_storage_.set_proxy_service(ProxyService::CreateFixed(proxy));
set_network_delegate(delegate);
Init();
}
~TestURLRequestContextWithProxy() override {}
};
// A mock ReportSenderInterface that just remembers the latest report
// URI and report to be sent.
class MockCertificateReportSender
: public TransportSecurityState::ReportSenderInterface {
public:
MockCertificateReportSender() {}
~MockCertificateReportSender() override {}
void Send(const GURL& report_uri,
base::StringPiece content_type,
base::StringPiece report,
const base::Callback<void()>& success_callback,
const base::Callback<void(const GURL&, int, int)>& error_callback)
override {
latest_report_uri_ = report_uri;
report.CopyToString(&latest_report_);
content_type.CopyToString(&latest_content_type_);
}
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_;
};
class TestExperimentalFeaturesNetworkDelegate : public TestNetworkDelegate {
public:
bool OnAreExperimentalCookieFeaturesEnabled() const override { return true; }
};
// 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,
const SSLInfo& ssl_info,
bool fatal) override {
ssl_info_ = ssl_info;
on_ssl_certificate_error_called_ = true;
TestDelegate::OnSSLCertificateError(request, 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:
URLRequestTest() : default_context_(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();
}
void SetUp() override {
SetUpFactory();
default_context_.set_job_factory(job_factory_.get());
default_context_.Init();
PlatformTest::SetUp();
}
virtual void SetUpFactory() {
job_factory_impl_->SetProtocolHandler(
"data", base::WrapUnique(new DataProtocolHandler));
#if !BUILDFLAG(DISABLE_FILE_SUPPORT)
job_factory_impl_->SetProtocolHandler(
"file", std::make_unique<FileProtocolHandler>(
base::ThreadTaskRunnerHandle::Get()));
#endif
}
TestNetworkDelegate* default_network_delegate() {
return &default_network_delegate_;
}
const 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_;
}
protected:
TestNetLog net_log_;
TestNetworkDelegate default_network_delegate_; // Must outlive URLRequest.
URLRequestJobFactoryImpl* job_factory_impl_;
std::unique_ptr<URLRequestJobFactory> job_factory_;
TestURLRequestContext default_context_;
};
// This NetworkDelegate is picky about what files are accessible. Only
// whitelisted files are allowed.
class CookieBlockingNetworkDelegate : public TestNetworkDelegate {
public:
CookieBlockingNetworkDelegate(){};
// Adds |directory| to the access white list.
void AddToWhitelist(const base::FilePath& directory) {
whitelist_.insert(directory);
}
private:
// Returns true if |path| matches the white list.
bool OnCanAccessFileInternal(const base::FilePath& path) const {
for (const auto& directory : whitelist_) {
if (directory == path || directory.IsParent(path))
return true;
}
return false;
}
// Returns true only if both |original_path| and |absolute_path| match the
// white list.
bool OnCanAccessFile(const URLRequest& request,
const base::FilePath& original_path,
const base::FilePath& absolute_path) const override {
return (OnCanAccessFileInternal(original_path) &&
OnCanAccessFileInternal(absolute_path));
}
std::set<base::FilePath> whitelist_;
DISALLOW_COPY_AND_ASSIGN(CookieBlockingNetworkDelegate);
};
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());
base::RunLoop().Run();
EXPECT_TRUE(!r->is_pending());
EXPECT_FALSE(d.received_data_before_response());
EXPECT_EQ(d.bytes_received(), 0);
EXPECT_EQ("", r->GetSocketAddress().host());
EXPECT_EQ(0, r->GetSocketAddress().port());
HttpRequestHeaders headers;
EXPECT_FALSE(r->GetFullRequestHeaders(&headers));
}
}
TEST_F(URLRequestTest, DataURLImageTest) {
TestDelegate d;
{
// Use our nice little Chrome logo.
std::unique_ptr<URLRequest> r(default_context_.CreateRequest(
GURL("data:image/png;base64,"
"iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAADVklEQVQ4jX2TfUwUB"
"BjG3w1y+HGcd9dxhXR8T4awOccJGgOSWclHImznLkTlSw0DDQXkrmgYgbUYnlQTqQ"
"xIEVxitD5UMCATRA1CEEg+Qjw3bWDxIauJv/5oumqs39/P827vnucRmYN0gyF01GI"
"5MpCVdW0gO7tvNC+vqSEtbZefk5NuLv1jdJ46p/zw0HeH4+PHr3h7c1mjoV2t5rKz"
"Mx1+fg9bAgK6zHq9cU5z+LpA3xOtx34+vTeT21onRuzssC3zxbbSwC13d/pFuC7Ck"
"IMDxQpF7r/MWq12UctI1dWWm99ypqSYmRUBdKem8MkrO/kgaTt1O7YzlpzE5GIVd0"
"WYUqt57yWf2McHTObYPbVD+ZwbtlLTVMZ3BW+TnLyXLaWtmEq6WJVbT3HBh3Svj2H"
"QQcm43XwmtoYM6vVKleh0uoWvnzW3v3MpidruPTQPf0bia7sJOtBM0ufTWNvus/nk"
"DFHF9ZS+uYVjRUasMeHUmyLYtcklTvzWGFZnNOXczThvpKIzjcahSqIzkvDLayDq6"
"D3eOjtBbNUEIZYyqsvj4V4wY92eNJ4IoyhTbxXX1T5xsV9tm9r4TQwHLiZw/pdDZJ"
"ea8TKmsmR/K0uLh/GwnCHghTja6lPhphezPfO5/5MrVvMzNaI3+ERHfrFzPKQukrQ"
"GI4d/3EFD/3E2mVNYvi4at7CXWREaxZGD+3hg28zD3gVMd6q5c8GdosynKmSeRuGz"
"pjyl1/9UDGtPR5HeaKT8Wjo17WXk579BXVUhN64ehF9fhRtq/uxxZKzNiZFGD0wRC"
"3NFROZ5mwIPL/96K/rKMMLrIzF9uhHr+/sYH7DAbwlgC4J+R2Z7FUx1qLnV7MGF40"
"smVSoJ/jvHRfYhQeUJd/SnYtGWhPHR0Sz+GE2F2yth0B36Vcz2KpnufBJbsysjjW4"
"kblBUiIjiURUWqJY65zxbnTy57GQyH58zgy0QBtTQv5gH15XMdKkYu+TGaJMnlm2O"
"34uI4b9tflqp1+QEFGzoW/ulmcofcpkZCYJhDfSpme7QcrHa+Xfji8paEQkTkSfmm"
"oRWRNZr/F1KfVMjW+IKEnv2FwZfKdzt0BQR6lClcZR0EfEXEfv/G6W9iLiIyCoReV"
"5EnhORIBHx+ufPj/gLB/zGI/G4Bk0AAAAASUVORK5CYII="),
DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS));
r->Start();
EXPECT_TRUE(r->is_pending());
base::RunLoop().Run();
EXPECT_TRUE(!r->is_pending());
EXPECT_FALSE(d.received_data_before_response());
EXPECT_EQ(d.bytes_received(), 911);
EXPECT_EQ("", r->GetSocketAddress().host());
EXPECT_EQ(0, r->GetSocketAddress().port());
HttpRequestHeaders headers;
EXPECT_FALSE(r->GetFullRequestHeaders(&headers));
}
}
#if !BUILDFLAG(DISABLE_FILE_SUPPORT)
TEST_F(URLRequestTest, FileTest) {
base::FilePath app_path;
#if defined(OS_ANDROID)
// Android devices are not guaranteed to be able to read /proc/self/exe
// Use /etc/hosts instead
app_path = base::FilePath("/etc/hosts");
#else
PathService::Get(base::FILE_EXE, &app_path);
#endif // OS_ANDROID
GURL app_url = FilePathToFileURL(app_path);
TestDelegate d;
{
std::unique_ptr<URLRequest> r(default_context_.CreateRequest(
app_url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS));
r->Start();
EXPECT_TRUE(r->is_pending());
base::RunLoop().Run();
int64_t file_size = -1;
EXPECT_TRUE(base::GetFileSize(app_path, &file_size));
EXPECT_TRUE(!r->is_pending());
EXPECT_EQ(1, d.response_started_count());
EXPECT_FALSE(d.received_data_before_response());
EXPECT_EQ(d.bytes_received(), static_cast<int>(file_size));
EXPECT_EQ("", r->GetSocketAddress().host());
EXPECT_EQ(0, r->GetSocketAddress().port());
HttpRequestHeaders headers;
EXPECT_FALSE(r->GetFullRequestHeaders(&headers));
}
}
TEST_F(URLRequestTest, FileTestCancel) {
base::FilePath app_path;
PathService::Get(base::FILE_EXE, &app_path);
GURL app_url = FilePathToFileURL(app_path);
TestDelegate d;
{
std::unique_ptr<URLRequest> r(default_context_.CreateRequest(
app_url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS));
r->Start();
EXPECT_TRUE(r->is_pending());
r->Cancel();
}
// Async cancellation should be safe even when URLRequest has been already
// destroyed.
base::RunLoop().RunUntilIdle();
}
TEST_F(URLRequestTest, FileTestFullSpecifiedRange) {
const size_t buffer_size = 4000;
std::unique_ptr<char[]> buffer(new char[buffer_size]);
FillBuffer(buffer.get(), buffer_size);
base::FilePath temp_path;
EXPECT_TRUE(base::CreateTemporaryFile(&temp_path));
GURL temp_url = FilePathToFileURL(temp_path);
EXPECT_EQ(static_cast<int>(buffer_size),
base::WriteFile(temp_path, buffer.get(), buffer_size));
int64_t file_size;
EXPECT_TRUE(base::GetFileSize(temp_path, &file_size));
const size_t first_byte_position = 500;
const size_t last_byte_position = buffer_size - first_byte_position;
const size_t content_length = last_byte_position - first_byte_position + 1;
std::string partial_buffer_string(buffer.get() + first_byte_position,
buffer.get() + last_byte_position + 1);
TestDelegate d;
{
std::unique_ptr<URLRequest> r(default_context_.CreateRequest(
temp_url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS));
HttpRequestHeaders headers;
headers.SetHeader(
HttpRequestHeaders::kRange,
HttpByteRange::Bounded(
first_byte_position, last_byte_position).GetHeaderValue());
r->SetExtraRequestHeaders(headers);
r->Start();
EXPECT_TRUE(r->is_pending());
base::RunLoop().Run();
EXPECT_TRUE(!r->is_pending());
EXPECT_EQ(1, d.response_started_count());
EXPECT_FALSE(d.received_data_before_response());
EXPECT_EQ(static_cast<int>(content_length), d.bytes_received());
// Don't use EXPECT_EQ, it will print out a lot of garbage if check failed.
EXPECT_TRUE(partial_buffer_string == d.data_received());
}
EXPECT_TRUE(base::DeleteFile(temp_path, false));
}
TEST_F(URLRequestTest, FileTestHalfSpecifiedRange) {
const size_t buffer_size = 4000;
std::unique_ptr<char[]> buffer(new char[buffer_size]);
FillBuffer(buffer.get(), buffer_size);
base::FilePath temp_path;
EXPECT_TRUE(base::CreateTemporaryFile(&temp_path));
GURL temp_url = FilePathToFileURL(temp_path);
EXPECT_TRUE(base::WriteFile(temp_path, buffer.get(), buffer_size));
int64_t file_size;
EXPECT_TRUE(base::GetFileSize(temp_path, &file_size));
const size_t first_byte_position = 500;
const size_t last_byte_position = buffer_size - 1;
const size_t content_length = last_byte_position - first_byte_position + 1;
std::string partial_buffer_string(buffer.get() + first_byte_position,
buffer.get() + last_byte_position + 1);
TestDelegate d;
{
std::unique_ptr<URLRequest> r(default_context_.CreateRequest(
temp_url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS));
HttpRequestHeaders headers;
headers.SetHeader(HttpRequestHeaders::kRange,
HttpByteRange::RightUnbounded(
first_byte_position).GetHeaderValue());
r->SetExtraRequestHeaders(headers);
r->Start();
EXPECT_TRUE(r->is_pending());
base::RunLoop().Run();
EXPECT_TRUE(!r->is_pending());
EXPECT_EQ(1, d.response_started_count());
EXPECT_FALSE(d.received_data_before_response());
EXPECT_EQ(static_cast<int>(content_length), d.bytes_received());
// Don't use EXPECT_EQ, it will print out a lot of garbage if check failed.
EXPECT_TRUE(partial_buffer_string == d.data_received());
}
EXPECT_TRUE(base::DeleteFile(temp_path, false));
}
TEST_F(URLRequestTest, FileTestMultipleRanges) {
const size_t buffer_size = 400000;
std::unique_ptr<char[]> buffer(new char[buffer_size]);
FillBuffer(buffer.get(), buffer_size);
base::FilePath temp_path;
EXPECT_TRUE(base::CreateTemporaryFile(&temp_path));
GURL temp_url = FilePathToFileURL(temp_path);
EXPECT_TRUE(base::WriteFile(temp_path, buffer.get(), buffer_size));
int64_t file_size;
EXPECT_TRUE(base::GetFileSize(temp_path, &file_size));
TestDelegate d;
{
std::unique_ptr<URLRequest> r(default_context_.CreateRequest(
temp_url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS));
HttpRequestHeaders headers;
headers.SetHeader(HttpRequestHeaders::kRange, "bytes=0-0,10-200,200-300");
r->SetExtraRequestHeaders(headers);
r->Start();
EXPECT_TRUE(r->is_pending());
base::RunLoop().Run();
EXPECT_TRUE(d.request_failed());
}
EXPECT_TRUE(base::DeleteFile(temp_path, false));
}
TEST_F(URLRequestTest, AllowFileURLs) {
base::ScopedTempDir temp_dir;
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());
base::FilePath test_file;
ASSERT_TRUE(base::CreateTemporaryFileInDir(absolute_temp_dir, &test_file));
// The directory part of the path returned from CreateTemporaryFileInDir()
// can be slightly different from |absolute_temp_dir| on Windows.
// Example: C:\\Users\\CHROME~2 -> C:\\Users\\chrome-bot
// Hence the test should use the directory name of |test_file|, rather than
// |absolute_temp_dir|, for whitelisting.
base::FilePath real_temp_dir = test_file.DirName();
std::string test_data("monkey");
base::WriteFile(test_file, test_data.data(), test_data.size());
GURL test_file_url = FilePathToFileURL(test_file);
{
TestDelegate d;
CookieBlockingNetworkDelegate network_delegate;
network_delegate.AddToWhitelist(real_temp_dir);
default_context_.set_network_delegate(&network_delegate);
std::unique_ptr<URLRequest> r(default_context_.CreateRequest(
test_file_url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS));
r->Start();
base::RunLoop().Run();
// This should be allowed as the file path is whitelisted.
EXPECT_FALSE(d.request_failed());
EXPECT_EQ(test_data, d.data_received());
}
{
TestDelegate d;
CookieBlockingNetworkDelegate network_delegate;
default_context_.set_network_delegate(&network_delegate);
std::unique_ptr<URLRequest> r(default_context_.CreateRequest(
test_file_url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS));
r->Start();
base::RunLoop().Run();
// This should be rejected as the file path is not whitelisted.
EXPECT_TRUE(d.request_failed());
EXPECT_EQ("", d.data_received());
EXPECT_EQ(ERR_ACCESS_DENIED, d.request_status());
}
}
#if defined(OS_POSIX) && !defined(OS_FUCHSIA) // Because of symbolic links.
TEST_F(URLRequestTest, SymlinksToFiles) {
base::ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
// Get an absolute path since temp_dir can contain a symbolic link.
base::FilePath absolute_temp_dir =
base::MakeAbsoluteFilePath(temp_dir.GetPath());
// Create a good directory (will be whitelisted) and a good file.
base::FilePath good_dir = absolute_temp_dir.AppendASCII("good");
ASSERT_TRUE(base::CreateDirectory(good_dir));
base::FilePath good_file;
ASSERT_TRUE(base::CreateTemporaryFileInDir(good_dir, &good_file));
std::string good_data("good");
base::WriteFile(good_file, good_data.data(), good_data.size());
// See the comment in AllowFileURLs() for why this is done.
base::FilePath real_good_dir = good_file.DirName();
// Create a bad directory (will not be whitelisted) and a bad file.
base::FilePath bad_dir = absolute_temp_dir.AppendASCII("bad");
ASSERT_TRUE(base::CreateDirectory(bad_dir));
base::FilePath bad_file;
ASSERT_TRUE(base::CreateTemporaryFileInDir(bad_dir, &bad_file));
std::string bad_data("bad");
base::WriteFile(bad_file, bad_data.data(), bad_data.size());
// This symlink will point to the good file. Access to the symlink will be
// allowed as both the symlink and the destination file are in the same
// good directory.
base::FilePath good_symlink = good_dir.AppendASCII("good_symlink");
ASSERT_TRUE(base::CreateSymbolicLink(good_file, good_symlink));
GURL good_file_url = FilePathToFileURL(good_symlink);
// This symlink will point to the bad file. Even though the symlink is in
// the good directory, access to the symlink will be rejected since it
// points to the bad file.
base::FilePath bad_symlink = good_dir.AppendASCII("bad_symlink");
ASSERT_TRUE(base::CreateSymbolicLink(bad_file, bad_symlink));
GURL bad_file_url = FilePathToFileURL(bad_symlink);
CookieBlockingNetworkDelegate network_delegate;
network_delegate.AddToWhitelist(real_good_dir);
{
TestDelegate d;
default_context_.set_network_delegate(&network_delegate);
std::unique_ptr<URLRequest> r(default_context_.CreateRequest(
good_file_url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS));
r->Start();
base::RunLoop().Run();
// good_file_url should be allowed.
EXPECT_FALSE(d.request_failed());
EXPECT_EQ(good_data, d.data_received());
}
{
TestDelegate d;
default_context_.set_network_delegate(&network_delegate);
std::unique_ptr<URLRequest> r(default_context_.CreateRequest(
bad_file_url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS));
r->Start();
base::RunLoop().Run();
// bad_file_url should be rejected.
EXPECT_TRUE(d.request_failed());
EXPECT_EQ("", d.data_received());
EXPECT_EQ(ERR_ACCESS_DENIED, d.request_status());
}
}
TEST_F(URLRequestTest, SymlinksToDirs) {
base::ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
// Get an absolute path since temp_dir can contain a symbolic link.
base::FilePath absolute_temp_dir =
base::MakeAbsoluteFilePath(temp_dir.GetPath());
// Create a good directory (will be whitelisted).
base::FilePath good_dir = absolute_temp_dir.AppendASCII("good");
ASSERT_TRUE(base::CreateDirectory(good_dir));
// Create a bad directory (will not be whitelisted).
base::FilePath bad_dir = absolute_temp_dir.AppendASCII("bad");
ASSERT_TRUE(base::CreateDirectory(bad_dir));
// This symlink will point to the good directory. Access to the symlink
// will be allowed as the symlink is in the good dir that'll be white
// listed.
base::FilePath good_symlink = good_dir.AppendASCII("good_symlink");
ASSERT_TRUE(base::CreateSymbolicLink(good_dir, good_symlink));
GURL good_file_url = FilePathToFileURL(good_symlink);
// This symlink will point to the bad directory. Even though the symlink is
// in the good directory, access to the symlink will be rejected since it
// points to the bad directory.
base::FilePath bad_symlink = good_dir.AppendASCII("bad_symlink");
ASSERT_TRUE(base::CreateSymbolicLink(bad_dir, bad_symlink));
GURL bad_file_url = FilePathToFileURL(bad_symlink);
CookieBlockingNetworkDelegate network_delegate;
network_delegate.AddToWhitelist(good_dir);
{
TestDelegate d;
default_context_.set_network_delegate(&network_delegate);
std::unique_ptr<URLRequest> r(default_context_.CreateRequest(
good_file_url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS));
r->Start();
base::RunLoop().Run();
// good_file_url should be allowed.
EXPECT_FALSE(d.request_failed());
ASSERT_NE(d.data_received().find("good_symlink"), std::string::npos);
}
{
TestDelegate d;
default_context_.set_network_delegate(&network_delegate);
std::unique_ptr<URLRequest> r(default_context_.CreateRequest(
bad_file_url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS));
r->Start();
base::RunLoop().Run();
// bad_file_url should be rejected.
EXPECT_TRUE(d.request_failed());
EXPECT_EQ("", d.data_received());
EXPECT_EQ(ERR_ACCESS_DENIED, d.request_status());
}
}
#endif // defined(OS_POSIX) && !defined(OS_FUCHSIA)
TEST_F(URLRequestTest, FileDirCancelTest) {
// Put in mock resource provider.
NetModule::SetResourceProvider(TestNetResourceProvider);
TestDelegate d;
{
base::FilePath file_path;
PathService::Get(base::DIR_SOURCE_ROOT, &file_path);
file_path = file_path.Append(FILE_PATH_LITERAL("net"));
file_path = file_path.Append(FILE_PATH_LITERAL("data"));
std::unique_ptr<URLRequest> req(default_context_.CreateRequest(
FilePathToFileURL(file_path), DEFAULT_PRIORITY, &d,
TRAFFIC_ANNOTATION_FOR_TESTS));
req->Start();
EXPECT_TRUE(req->is_pending());
d.set_cancel_in_received_data_pending(true);
base::RunLoop().Run();
}
// Take out mock resource provider.
NetModule::SetResourceProvider(NULL);
}
TEST_F(URLRequestTest, FileDirOutputSanity) {
// Verify the general sanity of the the output of the file:
// directory lister by checking for the output of a known existing
// file.
const char sentinel_name[] = "filedir-sentinel";
base::FilePath path;
PathService::Get(base::DIR_SOURCE_ROOT, &path);
path = path.Append(kTestFilePath);
TestDelegate d;
std::unique_ptr<URLRequest> req(
default_context_.CreateRequest(FilePathToFileURL(path), DEFAULT_PRIORITY,
&d, TRAFFIC_ANNOTATION_FOR_TESTS));
req->Start();
base::RunLoop().Run();
// Generate entry for the sentinel file.
base::FilePath sentinel_path = path.AppendASCII(sentinel_name);
base::File::Info info;
EXPECT_TRUE(base::GetFileInfo(sentinel_path, &info));
EXPECT_GT(info.size, 0);
std::string sentinel_output = GetDirectoryListingEntry(
base::string16(sentinel_name, sentinel_name + strlen(sentinel_name)),
std::string(sentinel_name), false /* is_dir */, info.size,
info.last_modified);
ASSERT_LT(0, d.bytes_received());
ASSERT_FALSE(d.request_failed());
EXPECT_EQ(OK, d.request_status());
// Check for the entry generated for the "sentinel" file.
const std::string& data = d.data_received();
ASSERT_NE(data.find(sentinel_output), std::string::npos);
}
TEST_F(URLRequestTest, FileDirRedirectNoCrash) {
// There is an implicit redirect when loading a file path that matches a
// directory and does not end with a slash. Ensure that following such
// redirects does not crash. See http://crbug.com/18686.
base::FilePath path;
PathService::Get(base::DIR_SOURCE_ROOT, &path);
path = path.Append(kTestFilePath);
TestDelegate d;
std::unique_ptr<URLRequest> req(
default_context_.CreateRequest(FilePathToFileURL(path), DEFAULT_PRIORITY,
&d, TRAFFIC_ANNOTATION_FOR_TESTS));
req->Start();
base::RunLoop().Run();
ASSERT_EQ(1, d.received_redirect_count());
ASSERT_LT(0, d.bytes_received());
ASSERT_FALSE(d.request_failed());
EXPECT_EQ(OK, d.request_status());
}
#if defined(OS_WIN)
// Don't accept the url "file:///" on windows. See http://crbug.com/1474.
TEST_F(URLRequestTest, FileDirRedirectSingleSlash) {
TestDelegate d;
std::unique_ptr<URLRequest> req(default_context_.CreateRequest(
GURL("file:///"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS));
req->Start();
base::RunLoop().Run();
ASSERT_EQ(1, d.received_redirect_count());
EXPECT_NE(OK, d.request_status());
}
#endif // defined(OS_WIN)
#endif // !BUILDFLAG(DISABLE_FILE_SUPPORT)
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());
base::RunLoop().Run();
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();
base::RunLoop().Run();
EXPECT_TRUE(d.request_failed());
}
#if defined(OS_WIN)
TEST_F(URLRequestTest, ResolveShortcutTest) {
base::FilePath app_path;
PathService::Get(base::DIR_SOURCE_ROOT, &app_path);
app_path = app_path.Append(kTestFilePath);
app_path = app_path.AppendASCII("with-headers.html");
std::wstring lnk_path = app_path.value() + L".lnk";
base::win::ScopedCOMInitializer com_initializer;
// Temporarily create a shortcut for test
{
base::win::ScopedComPtr<IShellLink> shell;
ASSERT_TRUE(SUCCEEDED(::CoCreateInstance(
CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&shell))));
base::win::ScopedComPtr<IPersistFile> persist;
ASSERT_TRUE(SUCCEEDED(shell.CopyTo(persist.GetAddressOf())));
EXPECT_TRUE(SUCCEEDED(shell->SetPath(app_path.value().c_str())));
EXPECT_TRUE(SUCCEEDED(shell->SetDescription(L"ResolveShortcutTest")));
EXPECT_TRUE(SUCCEEDED(persist->Save(lnk_path.c_str(), TRUE)));
}
TestDelegate d;
{
std::unique_ptr<URLRequest> r(default_context_.CreateRequest(
FilePathToFileURL(base::FilePath(lnk_path)), DEFAULT_PRIORITY, &d,
TRAFFIC_ANNOTATION_FOR_TESTS));
r->Start();
EXPECT_TRUE(r->is_pending());
base::RunLoop().Run();
WIN32_FILE_ATTRIBUTE_DATA data;
GetFileAttributesEx(app_path.value().c_str(),
GetFileExInfoStandard, &data);
HANDLE file = CreateFile(app_path.value().c_str(), GENERIC_READ,
FILE_SHARE_READ, NULL, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL, NULL);
EXPECT_NE(INVALID_HANDLE_VALUE, file);
std::unique_ptr<char[]> buffer(new char[data.nFileSizeLow]);
DWORD read_size;
BOOL result;
result = ReadFile(file, buffer.get(), data.nFileSizeLow,
&read_size, NULL);
std::string content(buffer.get(), read_size);
CloseHandle(file);
EXPECT_TRUE(!r->is_pending());
EXPECT_EQ(1, d.received_redirect_count());
EXPECT_EQ(content, d.data_received());
}
// Clean the shortcut
DeleteFile(lnk_path.c_str());
}
#endif // defined(OS_WIN)
// Custom URLRequestJobs for use with interceptor tests
class RestartTestJob : public URLRequestTestJob {
public:
RestartTestJob(URLRequest* request, NetworkDelegate* network_delegate)
: URLRequestTestJob(request, network_delegate, true) {}
protected:
void StartAsync() override { this->NotifyRestartRequired(); }
private:
~RestartTestJob() override {}
};
class CancelTestJob : public URLRequestTestJob {
public:
explicit CancelTestJob(URLRequest* request, NetworkDelegate* network_delegate)
: URLRequestTestJob(request, network_delegate, true) {}
protected:
void StartAsync() override { request_->Cancel(); }
private:
~CancelTestJob() override {}
};
class CancelThenRestartTestJob : public URLRequestTestJob {
public:
explicit CancelThenRestartTestJob(URLRequest* request,
NetworkDelegate* network_delegate)
: URLRequestTestJob(request, network_delegate, true) {
}
protected:
void StartAsync() override {
request_->Cancel();
this->NotifyRestartRequired();
}
private:
~CancelThenRestartTestJob() override {}
};
// An Interceptor for use with interceptor tests.
class MockURLRequestInterceptor : 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();
}
static std::string redirect_data() {
return std::string();
}
static std::string redirect_headers() {
return URLRequestTestJob::test_redirect_headers();
}
static std::string error_data() {
return std::string("ohhh nooooo mr. bill!");
}
static std::string error_headers() {
return URLRequestTestJob::test_error_headers();
}
MockURLRequestInterceptor()
: intercept_main_request_(false), restart_main_request_(false),
cancel_main_request_(false), cancel_then_restart_main_request_(false),
simulate_main_network_error_(false),
intercept_redirect_(false), cancel_redirect_request_(false),
intercept_final_response_(false), cancel_final_request_(false),
use_url_request_http_job_(false),
did_intercept_main_(false), did_restart_main_(false),
did_cancel_main_(false), did_cancel_then_restart_main_(false),
did_simulate_error_main_(false),
did_intercept_redirect_(false), did_cancel_redirect_(false),
did_intercept_final_(false), did_cancel_final_(false) {
}
~MockURLRequestInterceptor() override {
}
// URLRequestInterceptor implementation:
URLRequestJob* MaybeInterceptRequest(
URLRequest* request,
NetworkDelegate* network_delegate) const override {
if (restart_main_request_) {
restart_main_request_ = false;
did_restart_main_ = true;
return new RestartTestJob(request, network_delegate);
}
if (cancel_main_request_) {
cancel_main_request_ = false;
did_cancel_main_ = true;
return new CancelTestJob(request, network_delegate);
}
if (cancel_then_restart_main_request_) {
cancel_then_restart_main_request_ = false;
did_cancel_then_restart_main_ = true;
return new CancelThenRestartTestJob(request, network_delegate);
}
if (simulate_main_network_error_) {
simulate_main_network_error_ = false;
did_simulate_error_main_ = true;
if (use_url_request_http_job_) {
return URLRequestHttpJob::Factory(request, network_delegate, "http");
}
// This job will result in error since the requested URL is not one of the
// URLs supported by these tests.
return new URLRequestTestJob(request, network_delegate, true);
}
if (!intercept_main_request_)
return nullptr;
intercept_main_request_ = false;
did_intercept_main_ = true;
URLRequestTestJob* job = new URLRequestTestJob(request,
network_delegate,
main_headers_,
main_data_,
true);
job->set_load_timing_info(main_request_load_timing_info_);
return job;
}
URLRequestJob* MaybeInterceptRedirect(URLRequest* request,
NetworkDelegate* network_delegate,
const GURL& location) const override {
if (cancel_redirect_request_) {
cancel_redirect_request_ = false;
did_cancel_redirect_ = true;
return new CancelTestJob(request, network_delegate);
}
if (!intercept_redirect_)
return nullptr;
intercept_redirect_ = false;
did_intercept_redirect_ = true;
if (use_url_request_http_job_) {
return URLRequestHttpJob::Factory(request, network_delegate, "http");
}
return new URLRequestTestJob(request,
network_delegate,
redirect_headers_,
redirect_data_,
true);
}
URLRequestJob* MaybeInterceptResponse(
URLRequest* request,
NetworkDelegate* network_delegate) const override {
if (cancel_final_request_) {
cancel_final_request_ = false;
did_cancel_final_ = true;
return new CancelTestJob(request, network_delegate);
}
if (!intercept_final_response_)
return nullptr;
intercept_final_response_ = false;
did_intercept_final_ = true;
if (use_url_request_http_job_) {
return URLRequestHttpJob::Factory(request, network_delegate, "http");
}
return new URLRequestTestJob(request,
network_delegate,
final_headers_,
final_data_,
true);
}
void set_intercept_main_request(bool intercept_main_request) {
intercept_main_request_ = intercept_main_request;
}
void set_main_headers(const std::string& main_headers) {
main_headers_ = main_headers;
}
void set_main_data(const std::string& main_data) {
main_data_ = main_data;
}
void set_main_request_load_timing_info(
const LoadTimingInfo& main_request_load_timing_info) {
main_request_load_timing_info_ = main_request_load_timing_info;
}
void set_restart_main_request(bool restart_main_request) {
restart_main_request_ = restart_main_request;
}
void set_cancel_main_request(bool cancel_main_request) {
cancel_main_request_ = cancel_main_request;
}
void set_cancel_then_restart_main_request(
bool cancel_then_restart_main_request) {
cancel_then_restart_main_request_ = cancel_then_restart_main_request;
}
void set_simulate_main_network_error(bool simulate_main_network_error) {
simulate_main_network_error_ = simulate_main_network_error;
}
void set_intercept_redirect(bool intercept_redirect) {
intercept_redirect_ = intercept_redirect;
}
void set_redirect_headers(const std::string& redirect_headers) {
redirect_headers_ = redirect_headers;
}
void set_redirect_data(const std::string& redirect_data) {
redirect_data_ = redirect_data;
}
void set_cancel_redirect_request(bool cancel_redirect_request) {
cancel_redirect_request_ = cancel_redirect_request;
}
void set_intercept_final_response(bool intercept_final_response) {
intercept_final_response_ = intercept_final_response;
}
void set_final_headers(const std::string& final_headers) {
final_headers_ = final_headers;
}
void set_final_data(const std::string& final_data) {
final_data_ = final_data;
}
void set_cancel_final_request(bool cancel_final_request) {
cancel_final_request_ = cancel_final_request;
}
void set_use_url_request_http_job(bool use_url_request_http_job) {
use_url_request_http_job_ = use_url_request_http_job;
}
bool did_intercept_main() const {
return did_intercept_main_;
}
bool did_restart_main() const {
return did_restart_main_;
}
bool did_cancel_main() const {
return did_cancel_main_;
}
bool did_cancel_then_restart_main() const {
return did_cancel_then_restart_main_;
}
bool did_simulate_error_main() const {
return did_simulate_error_main_;
}
bool did_intercept_redirect() const {
return did_intercept_redirect_;
}
bool did_cancel_redirect() const {
return did_cancel_redirect_;
}
bool did_intercept_final() const {
return did_intercept_final_;
}
bool did_cancel_final() const {
return did_cancel_final_;
}
private:
// Indicate whether to intercept the main request, and if so specify the
// response to return and the LoadTimingInfo to use.
mutable bool intercept_main_request_;
mutable std::string main_headers_;
mutable std::string main_data_;
mutable LoadTimingInfo main_request_load_timing_info_;
// These indicate actions that can be taken within MaybeInterceptRequest.
mutable bool restart_main_request_;
mutable bool cancel_main_request_;
mutable bool cancel_then_restart_main_request_;
mutable bool simulate_main_network_error_;
// Indicate whether to intercept redirects, and if so specify the response to
// return.
mutable bool intercept_redirect_;
mutable std::string redirect_headers_;
mutable std::string redirect_data_;
// Cancel the request within MaybeInterceptRedirect.
mutable bool cancel_redirect_request_;
// Indicate whether to intercept the final response, and if so specify the
// response to return.
mutable bool intercept_final_response_;
mutable std::string final_headers_;
mutable std::string final_data_;
// Cancel the final request within MaybeInterceptResponse.
mutable bool cancel_final_request_;
// Instruct the interceptor to use a real URLRequestHTTPJob.
mutable bool use_url_request_http_job_;
// These indicate if the interceptor did something or not.
mutable bool did_intercept_main_;
mutable bool did_restart_main_;
mutable bool did_cancel_main_;
mutable bool did_cancel_then_restart_main_;
mutable bool did_simulate_error_main_;
mutable bool did_intercept_redirect_;
mutable bool did_cancel_redirect_;
mutable bool did_intercept_final_;
mutable bool did_cancel_final_;
};
// Inherit PlatformTest since we require the autorelease pool on Mac OS X.
class URLRequestInterceptorTest : public URLRequestTest {
public:
URLRequestInterceptorTest() : URLRequestTest(), interceptor_(NULL) {
}
~URLRequestInterceptorTest() override {
// URLRequestJobs may post clean-up tasks on destruction.
base::RunLoop().RunUntilIdle();
}
void SetUpFactory() override {
interceptor_ = new MockURLRequestInterceptor();
job_factory_.reset(new URLRequestInterceptingJobFactory(
std::move(job_factory_), base::WrapUnique(interceptor_)));
}
MockURLRequestInterceptor* interceptor() const {
return interceptor_;
}
private:
MockURLRequestInterceptor* interceptor_;
};
TEST_F(URLRequestInterceptorTest, Intercept) {
// Intercept the main request and respond with a simple response.
interceptor()->set_intercept_main_request(true);
interceptor()->set_main_headers(MockURLRequestInterceptor::ok_headers());
interceptor()->set_main_data(MockURLRequestInterceptor::ok_data());
TestDelegate d;
std::unique_ptr<URLRequest> req(default_context().CreateRequest(
GURL("http://test_intercept/foo"), DEFAULT_PRIORITY, &d,
TRAFFIC_ANNOTATION_FOR_TESTS));
base::SupportsUserData::Data* user_data0 = new base::SupportsUserData::Data();
base::SupportsUserData::Data* user_data1 = new base::SupportsUserData::Data();
base::SupportsUserData::Data* user_data2 = new base::SupportsUserData::Data();
req->SetUserData(&user_data0, base::WrapUnique(user_data0));
req->SetUserData(&user_data1, base::WrapUnique(user_data1));
req->SetUserData(&user_data2, base::WrapUnique(user_data2));
req->set_method("GET");
req->Start();
base::RunLoop().Run();
// Make sure we can retrieve our specific user data.
EXPECT_EQ(user_data0, req->GetUserData(&user_data0));
EXPECT_EQ(user_data1, req->GetUserData(&user_data1));
EXPECT_EQ(user_data2, req->GetUserData(&user_data2));
// Check that we got one good response.
EXPECT_EQ(OK, d.request_status());
EXPECT_EQ(200, req->response_headers()->response_code());
EXPECT_EQ(MockURLRequestInterceptor::ok_data(), d.data_received());
EXPECT_EQ(1, d.response_started_count());
EXPECT_EQ(0, d.received_redirect_count());
}
TEST_F(URLRequestInterceptorTest, InterceptRedirect) {
// Intercept the main request and respond with a redirect.
interceptor()->set_intercept_main_request(true);
interceptor()->set_main_headers(
MockURLRequestInterceptor::redirect_headers());
interceptor()->set_main_data(MockURLRequestInterceptor::redirect_data());
// Intercept that redirect and respond with a final OK response.
interceptor()->set_intercept_redirect(true);
interceptor()->set_redirect_headers(MockURLRequestInterceptor::ok_headers());
interceptor()->set_redirect_data(MockURLRequestInterceptor::ok_data());
TestDelegate d;
std::unique_ptr<URLRequest> req(default_context().CreateRequest(
GURL("http://test_intercept/foo"), DEFAULT_PRIORITY, &d,
TRAFFIC_ANNOTATION_FOR_TESTS));
req->set_method("GET");
req->Start();
base::RunLoop().Run();
// Check that the interceptor got called as expected.
EXPECT_TRUE(interceptor()->did_intercept_main());
EXPECT_TRUE(interceptor()->did_intercept_redirect());
// Check that we got one good response.
int status = d.request_status();
EXPECT_EQ(OK, status);
if (status == OK)
EXPECT_EQ(200, req->response_headers()->response_code());
EXPECT_EQ(MockURLRequestInterceptor::ok_data(), d.data_received());
EXPECT_EQ(1, d.response_started_count());
EXPECT_EQ(0, d.received_redirect_count());
}
TEST_F(URLRequestInterceptorTest, InterceptServerError) {
// Intercept the main request to generate a server error response.
interceptor()->set_intercept_main_request(true);
interceptor()->set_main_headers(MockURLRequestInterceptor::error_headers());
interceptor()->set_main_data(MockURLRequestInterceptor::error_data());
// Intercept that error and respond with an OK response.
interceptor()->set_intercept_final_response(true);
interceptor()->set_final_headers(MockURLRequestInterceptor::ok_headers());
interceptor()->set_final_data(MockURLRequestInterceptor::ok_data());
TestDelegate d;
std::unique_ptr<URLRequest> req(default_context().CreateRequest(
GURL("http://test_intercept/foo"), DEFAULT_PRIORITY, &d,
TRAFFIC_ANNOTATION_FOR_TESTS));
req->set_method("GET");
req->Start();
base::RunLoop().Run();
// Check that the interceptor got called as expected.
EXPECT_TRUE(interceptor()->did_intercept_main());
EXPECT_TRUE(interceptor()->did_intercept_final());
// Check that we got one good response.
EXPECT_EQ(OK, d.request_status());
EXPECT_EQ(200, req->response_headers()->response_code());
EXPECT_EQ(MockURLRequestInterceptor::ok_data(), d.data_received());
EXPECT_EQ(1, d.response_started_count());
EXPECT_EQ(0, d.received_redirect_count());
}
TEST_F(URLRequestInterceptorTest, InterceptNetworkError) {
// Intercept the main request to simulate a network error.
interceptor()->set_simulate_main_network_error(true);
// Intercept that error and respond with an OK response.
interceptor()->set_intercept_final_response(true);
interceptor()->set_final_headers(MockURLRequestInterceptor::ok_headers());
interceptor()->set_final_data(MockURLRequestInterceptor::ok_data());
TestDelegate d;
std::unique_ptr<URLRequest> req(default_context().CreateRequest(
GURL("http://test_intercept/foo"), DEFAULT_PRIORITY, &d,
TRAFFIC_ANNOTATION_FOR_TESTS));
req->set_method("GET");
req->Start();
base::RunLoop().Run();
// Check that the interceptor got called as expected.
EXPECT_TRUE(interceptor()->did_simulate_error_main());
EXPECT_TRUE(interceptor()->did_intercept_final());
// Check that we received one good response.
EXPECT_EQ(OK, d.request_status());
EXPECT_EQ(200, req->response_headers()->response_code());
EXPECT_EQ(MockURLRequestInterceptor::ok_data(), d.data_received());
EXPECT_EQ(1, d.response_started_count());
EXPECT_EQ(0, d.received_redirect_count());
}
TEST_F(URLRequestInterceptorTest, InterceptRestartRequired) {
// Restart the main request.
interceptor()->set_restart_main_request(true);
// then intercept the new main request and respond with an OK response
interceptor()->set_intercept_main_request(true);
interceptor()->set_main_headers(MockURLRequestInterceptor::ok_headers());
interceptor()->set_main_data(MockURLRequestInterceptor::ok_data());
TestDelegate d;
std::unique_ptr<URLRequest> req(default_context().CreateRequest(
GURL("http://test_intercept/foo"), DEFAULT_PRIORITY, &d,
TRAFFIC_ANNOTATION_FOR_TESTS));
req->set_method("GET");
req->Start();
base::RunLoop().Run();
// Check that the interceptor got called as expected.
EXPECT_TRUE(interceptor()->did_restart_main());
EXPECT_TRUE(interceptor()->did_intercept_main());
// Check that we received one good response.
int status = d.request_status();
EXPECT_EQ(OK, status);
if (status == OK)
EXPECT_EQ(200, req->response_headers()->response_code());
EXPECT_EQ(MockURLRequestInterceptor::ok_data(), d.data_received());
EXPECT_EQ(1, d.response_started_count());
EXPECT_EQ(0, d.received_redirect_count());
}
TEST_F(URLRequestInterceptorTest, InterceptRespectsCancelMain) {
// Intercept the main request and cancel from within the restarted job.
interceptor()->set_cancel_main_request(true);
// Set up to intercept the final response and override it with an OK response.
interceptor()->set_intercept_final_response(true);
interceptor()->set_final_headers(MockURLRequestInterceptor::ok_headers());
interceptor()->set_final_data(MockURLRequestInterceptor::ok_data());
TestDelegate d;
std::unique_ptr<URLRequest> req(default_context().CreateRequest(
GURL("http://test_intercept/foo"), DEFAULT_PRIORITY, &d,
TRAFFIC_ANNOTATION_FOR_TESTS));
req->set_method("GET");
req->Start();
base::RunLoop().Run();
// Check that the interceptor got called as expected.
EXPECT_TRUE(interceptor()->did_cancel_main());
EXPECT_FALSE(interceptor()->did_intercept_final());
// Check that we see a canceled request.
EXPECT_EQ(ERR_ABORTED, d.request_status());
}
TEST_F(URLRequestInterceptorTest, InterceptRespectsCancelRedirect) {
// Intercept the main request and respond with a redirect.
interceptor()->set_intercept_main_request(true);
interceptor()->set_main_headers(
MockURLRequestInterceptor::redirect_headers());
interceptor()->set_main_data(MockURLRequestInterceptor::redirect_data());
// Intercept the redirect and cancel from within that job.
interceptor()->set_cancel_redirect_request(true);
// Set up to intercept the final response and override it with an OK response.
interceptor()->set_intercept_final_response(true);
interceptor()->set_final_headers(MockURLRequestInterceptor::ok_headers());
interceptor()->set_final_data(MockURLRequestInterceptor::ok_data());
TestDelegate d;
std::unique_ptr<URLRequest> req(default_context().CreateRequest(
GURL("http://test_intercept/foo"), DEFAULT_PRIORITY, &d,
TRAFFIC_ANNOTATION_FOR_TESTS));
req->set_method("GET");
req->Start();
base::RunLoop().Run();
// Check that the interceptor got called as expected.
EXPECT_TRUE(interceptor()->did_intercept_main());
EXPECT_TRUE(interceptor()->did_cancel_redirect());
EXPECT_FALSE(interceptor()->did_intercept_final());
// Check that we see a canceled request.
EXPECT_EQ(ERR_ABORTED, d.request_status());
}
TEST_F(URLRequestInterceptorTest, InterceptRespectsCancelFinal) {
// Intercept the main request to simulate a network error.
interceptor()->set_simulate_main_network_error(true);
// Set up to intercept final the response and cancel from within that job.
interceptor()->set_cancel_final_request(true);
TestDelegate d;
std::unique_ptr<URLRequest> req(default_context().CreateRequest(
GURL("http://test_intercept/foo"), DEFAULT_PRIORITY, &d,
TRAFFIC_ANNOTATION_FOR_TESTS));
req->set_method("GET");
req->Start();
base::RunLoop().Run();
// Check that the interceptor got called as expected.
EXPECT_TRUE(interceptor()->did_simulate_error_main());
EXPECT_TRUE(interceptor()->did_cancel_final());
// Check that we see a canceled request.
EXPECT_EQ(ERR_ABORTED, d.request_status());
}
TEST_F(URLRequestInterceptorTest, InterceptRespectsCancelInRestart) {
// Intercept the main request and cancel then restart from within that job.
interceptor()->set_cancel_then_restart_main_request(true);
// Set up to intercept the final response and override it with an OK response.
interceptor()->set_intercept_final_response(true);
interceptor()->set_final_headers(MockURLRequestInterceptor::ok_headers());
interceptor()->set_final_data(MockURLRequestInterceptor::ok_data());
TestDelegate d;
std::unique_ptr<URLRequest> req(default_context().CreateRequest(
GURL("http://test_intercept/foo"), DEFAULT_PRIORITY, &d,
TRAFFIC_ANNOTATION_FOR_TESTS));
req->set_method("GET");
req->Start();
base::RunLoop().Run();
// Check that the interceptor got called as expected.
EXPECT_TRUE(interceptor()->did_cancel_then_restart_main());
EXPECT_FALSE(interceptor()->did_intercept_final());
// Check that we see a canceled request.
EXPECT_EQ(ERR_ABORTED, d.request_status());
}
// "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_end = now + base::TimeDelta::FromDays(11);
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_end = now + base::TimeDelta::FromDays(11);
return load_timing;
}
LoadTimingInfo RunURLRequestInterceptorLoadTimingTest(
const LoadTimingInfo& job_load_timing,
const URLRequestContext& context,
MockURLRequestInterceptor* interceptor) {
interceptor->set_intercept_main_request(true);
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();
base::RunLoop().Run();
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_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(URLRequestInterceptorTest, 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(URLRequestInterceptorTest, 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(URLRequestInterceptorTest, 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(URLRequestInterceptorTest,
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(URLRequestInterceptorTest, 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(URLRequestInterceptorTest, 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);
}
// Check that two different URL requests have different identifiers.
TEST_F(URLRequestTest, Identifiers) {
TestDelegate d;
TestURLRequestContext context;
std::unique_ptr<URLRequest> req(
context.CreateRequest(GURL("http://example.com"), DEFAULT_PRIORITY, &d,
TRAFFIC_ANNOTATION_FOR_TESTS));
std::unique_ptr<URLRequest> other_req(
context.CreateRequest(GURL("http://example.com"), DEFAULT_PRIORITY, &d,
TRAFFIC_ANNOTATION_FOR_TESTS));
ASSERT_NE(req->identifier(), other_req->identifier());
}
#if defined(OS_IOS)
// TODO(droger): Check that a failure to connect to the proxy is reported to
// the network delegate. crbug.com/496743
#define MAYBE_NetworkDelegateProxyError DISABLED_NetworkDelegateProxyError
#else
#define MAYBE_NetworkDelegateProxyError NetworkDelegateProxyError
#endif
TEST_F(URLRequestTest, MAYBE_NetworkDelegateProxyError) {
MockHostResolver host_resolver;
host_resolver.rules()->AddSimulatedFailure("*");
TestNetworkDelegate network_delegate; // Must outlive URLRequests.
TestURLRequestContextWithProxy context("myproxy:70", &network_delegate);
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();
base::RunLoop().Run();
// Check we see a failed request.
// The proxy server is not set before failure.
EXPECT_FALSE(req->proxy_server().is_valid());
EXPECT_EQ(ERR_PROXY_CONNECTION_FAILED, d.request_status());
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());
}
// Make sure that NetworkDelegate::NotifyCompleted is called if
// content is empty.
TEST_F(URLRequestTest, RequestCompletionForEmptyResponse) {
TestDelegate d;
std::unique_ptr<URLRequest> req(default_context_.CreateRequest(
GURL("data:,"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS));
req->Start();
base::RunLoop().Run();
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);
}
namespace {
// Less verbose way of running a simple testserver for the tests below.
class LocalHttpTestServer : public EmbeddedTestServer {
public:
explicit LocalHttpTestServer(const base::FilePath& document_root) {
AddDefaultHandlers(document_root);
}
LocalHttpTestServer() { AddDefaultHandlers(base::FilePath()); }
};
} // namespace
TEST_F(URLRequestTest, DelayedCookieCallback) {
LocalHttpTestServer 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.CreateRequest(
test_server.GetURL("/set-cookie?CookieToNotSend=1"), DEFAULT_PRIORITY,
&d, TRAFFIC_ANNOTATION_FOR_TESTS));
req->Start();
base::RunLoop().Run();
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.CreateRequest(
test_server.GetURL("/echoheader?Cookie"), DEFAULT_PRIORITY, &d,
TRAFFIC_ANNOTATION_FOR_TESTS));
req->Start();
base::RunLoop().Run();
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());
}
}
TEST_F(URLRequestTest, DoNotSendCookies) {
LocalHttpTestServer 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_.CreateRequest(
test_server.GetURL("/set-cookie?CookieToNotSend=1"), DEFAULT_PRIORITY,
&d, TRAFFIC_ANNOTATION_FOR_TESTS));
req->Start();
base::RunLoop().Run();
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_.CreateRequest(
test_server.GetURL("/echoheader?Cookie"), DEFAULT_PRIORITY, &d,
TRAFFIC_ANNOTATION_FOR_TESTS));
req->Start();
base::RunLoop().Run();
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_.CreateRequest(
test_server.GetURL("/echoheader?Cookie"), DEFAULT_PRIORITY, &d,
TRAFFIC_ANNOTATION_FOR_TESTS));
req->SetLoadFlags(LOAD_DO_NOT_SEND_COOKIES);
req->Start();
base::RunLoop().Run();
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) {
LocalHttpTestServer 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_.CreateRequest(
test_server.GetURL("/set-cookie?CookieToNotUpdate=2"), DEFAULT_PRIORITY,
&d, TRAFFIC_ANNOTATION_FOR_TESTS));
req->Start();
base::RunLoop().Run();
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_.CreateRequest(
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();
base::RunLoop().Run();
// 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_.CreateRequest(
test_server.GetURL("/echoheader?Cookie"), DEFAULT_PRIORITY, &d,
TRAFFIC_ANNOTATION_FOR_TESTS));
req->Start();
base::RunLoop().Run();
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) {
LocalHttpTestServer 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_.CreateRequest(
test_server.GetURL("/set-cookie?CookieToNotSend=1"), DEFAULT_PRIORITY,
&d, TRAFFIC_ANNOTATION_FOR_TESTS));
req->Start();
base::RunLoop().Run();
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_.CreateRequest(
test_server.GetURL("/echoheader?Cookie"), DEFAULT_PRIORITY, &d,
TRAFFIC_ANNOTATION_FOR_TESTS));
req->Start();