blob: b425b376e7e837794e781fcb10fcf107b85f4cdd [file] [log] [blame]
// Copyright 2013 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "net/url_request/url_request_http_job.h"
#include <stdint.h>
#include <cstddef>
#include <memory>
#include <utility>
#include <vector>
#include "base/compiler_specific.h"
#include "base/memory/ptr_util.h"
#include "base/memory/ref_counted.h"
#include "base/run_loop.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h"
#include "base/test/bind.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/task_environment.h"
#include "build/build_config.h"
#include "net/base/auth.h"
#include "net/base/features.h"
#include "net/base/isolation_info.h"
#include "net/base/load_flags.h"
#include "net/base/proxy_chain.h"
#include "net/base/proxy_server.h"
#include "net/base/proxy_string_util.h"
#include "net/base/request_priority.h"
#include "net/cert/ct_policy_status.h"
#include "net/cookies/canonical_cookie_test_helpers.h"
#include "net/cookies/cookie_monster.h"
#include "net/cookies/cookie_store_test_callbacks.h"
#include "net/cookies/cookie_store_test_helpers.h"
#include "net/cookies/test_cookie_access_delegate.h"
#include "net/http/http_transaction_factory.h"
#include "net/http/http_transaction_test_util.h"
#include "net/http/transport_security_state.h"
#include "net/log/net_log_event_type.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/socket/next_proto.h"
#include "net/socket/socket_test_util.h"
#include "net/test/cert_test_util.h"
#include "net/test/embedded_test_server/default_handlers.h"
#include "net/test/gtest_util.h"
#include "net/test/test_data_directory.h"
#include "net/test/test_with_task_environment.h"
#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
#include "net/url_request/url_request.h"
#include "net/url_request/url_request_context.h"
#include "net/url_request/url_request_context_builder.h"
#include "net/url_request/url_request_test_util.h"
#include "net/url_request/websocket_handshake_userdata_key.h"
#include "net/websockets/websocket_test_util.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
#include "url/url_constants.h"
#if BUILDFLAG(IS_ANDROID)
#include "base/android/jni_android.h"
#include "net/android/net_test_support_jni/AndroidNetworkLibraryTestUtil_jni.h"
#endif
using net::test::IsError;
using net::test::IsOk;
namespace net {
namespace {
using ::testing::_;
using ::testing::Return;
using ::testing::UnorderedElementsAre;
const char kSimpleGetMockWrite[] =
"GET / HTTP/1.1\r\n"
"Host: www.example.com\r\n"
"Connection: keep-alive\r\n"
"User-Agent: \r\n"
"Accept-Encoding: gzip, deflate\r\n"
"Accept-Language: en-us,fr\r\n\r\n";
const char kSimpleHeadMockWrite[] =
"HEAD / HTTP/1.1\r\n"
"Host: www.example.com\r\n"
"Connection: keep-alive\r\n"
"User-Agent: \r\n"
"Accept-Encoding: gzip, deflate\r\n"
"Accept-Language: en-us,fr\r\n\r\n";
const char kTrustAnchorRequestHistogram[] =
"Net.Certificate.TrustAnchor.Request";
// Inherit from URLRequestHttpJob to expose the priority and some
// other hidden functions.
class TestURLRequestHttpJob : public URLRequestHttpJob {
public:
explicit TestURLRequestHttpJob(URLRequest* request)
: URLRequestHttpJob(request,
request->context()->http_user_agent_settings()) {}
TestURLRequestHttpJob(const TestURLRequestHttpJob&) = delete;
TestURLRequestHttpJob& operator=(const TestURLRequestHttpJob&) = delete;
~TestURLRequestHttpJob() override = default;
// URLRequestJob implementation:
std::unique_ptr<SourceStream> SetUpSourceStream() override {
if (use_null_source_stream_)
return nullptr;
return URLRequestHttpJob::SetUpSourceStream();
}
void set_use_null_source_stream(bool use_null_source_stream) {
use_null_source_stream_ = use_null_source_stream;
}
using URLRequestHttpJob::SetPriority;
using URLRequestHttpJob::Start;
using URLRequestHttpJob::Kill;
using URLRequestHttpJob::priority;
private:
bool use_null_source_stream_ = false;
};
class URLRequestHttpJobSetUpSourceTest : public TestWithTaskEnvironment {
public:
URLRequestHttpJobSetUpSourceTest() {
auto context_builder = CreateTestURLRequestContextBuilder();
context_builder->set_client_socket_factory_for_testing(&socket_factory_);
context_ = context_builder->Build();
}
protected:
MockClientSocketFactory socket_factory_;
std::unique_ptr<URLRequestContext> context_;
TestDelegate delegate_;
};
// Tests that if SetUpSourceStream() returns nullptr, the request fails.
TEST_F(URLRequestHttpJobSetUpSourceTest, SetUpSourceFails) {
MockWrite writes[] = {MockWrite(kSimpleGetMockWrite)};
MockRead reads[] = {MockRead("HTTP/1.1 200 OK\r\n"
"Content-Length: 12\r\n\r\n"),
MockRead("Test Content")};
StaticSocketDataProvider socket_data(reads, writes);
socket_factory_.AddSocketDataProvider(&socket_data);
std::unique_ptr<URLRequest> request =
context_->CreateRequest(GURL("http://www.example.com"), DEFAULT_PRIORITY,
&delegate_, TRAFFIC_ANNOTATION_FOR_TESTS);
auto job = std::make_unique<TestURLRequestHttpJob>(request.get());
job->set_use_null_source_stream(true);
TestScopedURLInterceptor interceptor(request->url(), std::move(job));
request->Start();
delegate_.RunUntilComplete();
EXPECT_EQ(ERR_CONTENT_DECODING_INIT_FAILED, delegate_.request_status());
}
// Tests that if there is an unknown content-encoding type, the raw response
// body is passed through.
TEST_F(URLRequestHttpJobSetUpSourceTest, UnknownEncoding) {
MockWrite writes[] = {MockWrite(kSimpleGetMockWrite)};
MockRead reads[] = {MockRead("HTTP/1.1 200 OK\r\n"
"Content-Encoding: foo, gzip\r\n"
"Content-Length: 12\r\n\r\n"),
MockRead("Test Content")};
StaticSocketDataProvider socket_data(reads, writes);
socket_factory_.AddSocketDataProvider(&socket_data);
std::unique_ptr<URLRequest> request =
context_->CreateRequest(GURL("http://www.example.com"), DEFAULT_PRIORITY,
&delegate_, TRAFFIC_ANNOTATION_FOR_TESTS);
auto job = std::make_unique<TestURLRequestHttpJob>(request.get());
TestScopedURLInterceptor interceptor(request->url(), std::move(job));
request->Start();
delegate_.RunUntilComplete();
EXPECT_EQ(OK, delegate_.request_status());
EXPECT_EQ("Test Content", delegate_.data_received());
}
// TaskEnvironment is required to instantiate a
// net::ConfiguredProxyResolutionService, which registers itself as an IP
// Address Observer with the NetworkChangeNotifier.
using URLRequestHttpJobWithProxyTest = TestWithTaskEnvironment;
class URLRequestHttpJobWithProxy {
public:
explicit URLRequestHttpJobWithProxy(
std::unique_ptr<ProxyResolutionService> proxy_resolution_service) {
auto context_builder = CreateTestURLRequestContextBuilder();
context_builder->set_client_socket_factory_for_testing(&socket_factory_);
if (proxy_resolution_service) {
context_builder->set_proxy_resolution_service(
std::move(proxy_resolution_service));
}
context_ = context_builder->Build();
}
URLRequestHttpJobWithProxy(const URLRequestHttpJobWithProxy&) = delete;
URLRequestHttpJobWithProxy& operator=(const URLRequestHttpJobWithProxy&) =
delete;
MockClientSocketFactory socket_factory_;
std::unique_ptr<URLRequestContext> context_;
};
// Tests that when a proxy is not used, the proxy chain is set correctly on the
// URLRequest.
TEST_F(URLRequestHttpJobWithProxyTest, TestFailureWithoutProxy) {
URLRequestHttpJobWithProxy http_job_with_proxy(nullptr);
MockWrite writes[] = {MockWrite(kSimpleGetMockWrite)};
MockRead reads[] = {MockRead(SYNCHRONOUS, ERR_CONNECTION_RESET)};
StaticSocketDataProvider socket_data(reads, writes);
http_job_with_proxy.socket_factory_.AddSocketDataProvider(&socket_data);
TestDelegate delegate;
std::unique_ptr<URLRequest> request =
http_job_with_proxy.context_->CreateRequest(
GURL("http://www.example.com"), DEFAULT_PRIORITY, &delegate,
TRAFFIC_ANNOTATION_FOR_TESTS);
request->Start();
ASSERT_TRUE(request->is_pending());
delegate.RunUntilComplete();
EXPECT_THAT(delegate.request_status(), IsError(ERR_CONNECTION_RESET));
EXPECT_EQ(ProxyChain::Direct(), request->proxy_chain());
EXPECT_EQ(0, request->received_response_content_length());
EXPECT_EQ(CountWriteBytes(writes), request->GetTotalSentBytes());
EXPECT_EQ(CountReadBytes(reads), request->GetTotalReceivedBytes());
}
// Tests that when one proxy chain is in use and the connection to a proxy
// server in the proxy chain fails, the proxy chain is still set correctly on
// the URLRequest.
TEST_F(URLRequestHttpJobWithProxyTest, TestSuccessfulWithOneProxy) {
const char kSimpleProxyGetMockWrite[] =
"GET http://www.example.com/ HTTP/1.1\r\n"
"Host: www.example.com\r\n"
"Proxy-Connection: keep-alive\r\n"
"User-Agent: \r\n"
"Accept-Encoding: gzip, deflate\r\n"
"Accept-Language: en-us,fr\r\n\r\n";
const ProxyChain proxy_chain =
ProxyUriToProxyChain("http://origin.net:80", ProxyServer::SCHEME_HTTP);
std::unique_ptr<ProxyResolutionService> proxy_resolution_service =
ConfiguredProxyResolutionService::CreateFixedFromPacResultForTest(
ProxyServerToPacResultElement(
proxy_chain.GetProxyServer(/*chain_index=*/0)),
TRAFFIC_ANNOTATION_FOR_TESTS);
MockWrite writes[] = {MockWrite(kSimpleProxyGetMockWrite)};
MockRead reads[] = {MockRead(SYNCHRONOUS, ERR_CONNECTION_RESET)};
StaticSocketDataProvider socket_data(reads, writes);
URLRequestHttpJobWithProxy http_job_with_proxy(
std::move(proxy_resolution_service));
http_job_with_proxy.socket_factory_.AddSocketDataProvider(&socket_data);
TestDelegate delegate;
std::unique_ptr<URLRequest> request =
http_job_with_proxy.context_->CreateRequest(
GURL("http://www.example.com"), DEFAULT_PRIORITY, &delegate,
TRAFFIC_ANNOTATION_FOR_TESTS);
request->Start();
ASSERT_TRUE(request->is_pending());
delegate.RunUntilComplete();
EXPECT_THAT(delegate.request_status(), IsError(ERR_CONNECTION_RESET));
// When request fails due to proxy connection errors, the proxy chain should
// still be set on the `request`.
EXPECT_EQ(proxy_chain, request->proxy_chain());
EXPECT_EQ(0, request->received_response_content_length());
EXPECT_EQ(CountWriteBytes(writes), request->GetTotalSentBytes());
EXPECT_EQ(0, request->GetTotalReceivedBytes());
}
// Tests that when two proxy chains are in use and the connection to a proxy
// server in the first proxy chain fails, the proxy chain is set correctly on
// the URLRequest.
TEST_F(URLRequestHttpJobWithProxyTest,
TestContentLengthSuccessfulRequestWithTwoProxies) {
const ProxyChain proxy_chain =
ProxyUriToProxyChain("http://origin.net:80", ProxyServer::SCHEME_HTTP);
// Connection to `proxy_chain` would fail. Request should be fetched over
// DIRECT.
std::unique_ptr<ProxyResolutionService> proxy_resolution_service =
ConfiguredProxyResolutionService::CreateFixedFromPacResultForTest(
ProxyServerToPacResultElement(
proxy_chain.GetProxyServer(/*chain_index=*/0)) +
"; DIRECT",
TRAFFIC_ANNOTATION_FOR_TESTS);
MockWrite writes[] = {MockWrite(kSimpleGetMockWrite)};
MockRead reads[] = {MockRead("HTTP/1.1 200 OK\r\n"
"Content-Length: 12\r\n\r\n"),
MockRead("Test Content"), MockRead(ASYNC, OK)};
MockConnect mock_connect_1(SYNCHRONOUS, ERR_CONNECTION_RESET);
StaticSocketDataProvider connect_data_1;
connect_data_1.set_connect_data(mock_connect_1);
StaticSocketDataProvider socket_data(reads, writes);
URLRequestHttpJobWithProxy http_job_with_proxy(
std::move(proxy_resolution_service));
http_job_with_proxy.socket_factory_.AddSocketDataProvider(&connect_data_1);
http_job_with_proxy.socket_factory_.AddSocketDataProvider(&socket_data);
TestDelegate delegate;
std::unique_ptr<URLRequest> request =
http_job_with_proxy.context_->CreateRequest(
GURL("http://www.example.com"), DEFAULT_PRIORITY, &delegate,
TRAFFIC_ANNOTATION_FOR_TESTS);
request->Start();
ASSERT_TRUE(request->is_pending());
delegate.RunUntilComplete();
EXPECT_THAT(delegate.request_status(), IsOk());
EXPECT_EQ(ProxyChain::Direct(), request->proxy_chain());
EXPECT_EQ(12, request->received_response_content_length());
EXPECT_EQ(CountWriteBytes(writes), request->GetTotalSentBytes());
EXPECT_EQ(CountReadBytes(reads), request->GetTotalReceivedBytes());
}
class URLRequestHttpJobTest : public TestWithTaskEnvironment {
protected:
URLRequestHttpJobTest() {
auto context_builder = CreateTestURLRequestContextBuilder();
context_builder->SetHttpTransactionFactoryForTesting(
std::make_unique<MockNetworkLayer>());
context_builder->DisableHttpCache();
context_builder->set_net_log(NetLog::Get());
context_ = context_builder->Build();
req_ = context_->CreateRequest(GURL("http://www.example.com"),
DEFAULT_PRIORITY, &delegate_,
TRAFFIC_ANNOTATION_FOR_TESTS);
}
MockNetworkLayer& network_layer() {
// This cast is safe because we set a MockNetworkLayer in the constructor.
return *static_cast<MockNetworkLayer*>(
context_->http_transaction_factory());
}
std::unique_ptr<URLRequest> CreateFirstPartyRequest(
const URLRequestContext& context,
const GURL& url,
URLRequest::Delegate* delegate) {
auto req = context.CreateRequest(url, DEFAULT_PRIORITY, delegate,
TRAFFIC_ANNOTATION_FOR_TESTS);
req->set_initiator(url::Origin::Create(url));
req->set_site_for_cookies(SiteForCookies::FromUrl(url));
return req;
}
std::unique_ptr<URLRequestContext> context_;
TestDelegate delegate_;
RecordingNetLogObserver net_log_observer_;
std::unique_ptr<URLRequest> req_;
};
class URLRequestHttpJobWithMockSocketsTest : public TestWithTaskEnvironment {
protected:
URLRequestHttpJobWithMockSocketsTest() {
auto context_builder = CreateTestURLRequestContextBuilder();
context_builder->set_client_socket_factory_for_testing(&socket_factory_);
context_ = context_builder->Build();
}
MockClientSocketFactory socket_factory_;
std::unique_ptr<URLRequestContext> context_;
};
TEST_F(URLRequestHttpJobWithMockSocketsTest,
TestContentLengthSuccessfulRequest) {
MockWrite writes[] = {MockWrite(kSimpleGetMockWrite)};
MockRead reads[] = {MockRead("HTTP/1.1 200 OK\r\n"
"Content-Length: 12\r\n\r\n"),
MockRead("Test Content")};
StaticSocketDataProvider socket_data(reads, writes);
socket_factory_.AddSocketDataProvider(&socket_data);
TestDelegate delegate;
std::unique_ptr<URLRequest> request =
context_->CreateRequest(GURL("http://www.example.com"), DEFAULT_PRIORITY,
&delegate, TRAFFIC_ANNOTATION_FOR_TESTS);
request->Start();
ASSERT_TRUE(request->is_pending());
delegate.RunUntilComplete();
EXPECT_THAT(delegate.request_status(), IsOk());
EXPECT_EQ(12, request->received_response_content_length());
EXPECT_EQ(CountWriteBytes(writes), request->GetTotalSentBytes());
EXPECT_EQ(CountReadBytes(reads), request->GetTotalReceivedBytes());
}
// Tests a successful HEAD request.
TEST_F(URLRequestHttpJobWithMockSocketsTest, TestSuccessfulHead) {
MockWrite writes[] = {MockWrite(kSimpleHeadMockWrite)};
MockRead reads[] = {
MockRead("HTTP/1.1 200 OK\r\n"
"Content-Length: 0\r\n\r\n")};
StaticSocketDataProvider socket_data(reads, writes);
socket_factory_.AddSocketDataProvider(&socket_data);
TestDelegate delegate;
std::unique_ptr<URLRequest> request =
context_->CreateRequest(GURL("http://www.example.com"), DEFAULT_PRIORITY,
&delegate, TRAFFIC_ANNOTATION_FOR_TESTS);
request->set_method("HEAD");
request->Start();
ASSERT_TRUE(request->is_pending());
delegate.RunUntilComplete();
EXPECT_THAT(delegate.request_status(), IsOk());
EXPECT_EQ(0, request->received_response_content_length());
EXPECT_EQ(CountWriteBytes(writes), request->GetTotalSentBytes());
EXPECT_EQ(CountReadBytes(reads), request->GetTotalReceivedBytes());
}
// Similar to above test but tests that even if response body is there in the
// HEAD response stream, it should not be read due to HttpStreamParser's logic.
TEST_F(URLRequestHttpJobWithMockSocketsTest, TestSuccessfulHeadWithContent) {
MockWrite writes[] = {MockWrite(kSimpleHeadMockWrite)};
MockRead reads[] = {MockRead("HTTP/1.1 200 OK\r\n"
"Content-Length: 12\r\n\r\n"),
MockRead("Test Content")};
StaticSocketDataProvider socket_data(reads, writes);
socket_factory_.AddSocketDataProvider(&socket_data);
TestDelegate delegate;
std::unique_ptr<URLRequest> request =
context_->CreateRequest(GURL("http://www.example.com"), DEFAULT_PRIORITY,
&delegate, TRAFFIC_ANNOTATION_FOR_TESTS);
request->set_method("HEAD");
request->Start();
ASSERT_TRUE(request->is_pending());
delegate.RunUntilComplete();
EXPECT_THAT(delegate.request_status(), IsOk());
EXPECT_EQ(0, request->received_response_content_length());
EXPECT_EQ(CountWriteBytes(writes), request->GetTotalSentBytes());
EXPECT_EQ(CountReadBytes(reads) - 12, request->GetTotalReceivedBytes());
}
TEST_F(URLRequestHttpJobWithMockSocketsTest, TestSuccessfulCachedHeadRequest) {
const url::Origin kOrigin1 =
url::Origin::Create(GURL("http://www.example.com"));
const IsolationInfo kTestIsolationInfo =
IsolationInfo::CreateForInternalRequest(kOrigin1);
// Cache the response.
{
MockWrite writes[] = {MockWrite(kSimpleGetMockWrite)};
MockRead reads[] = {MockRead("HTTP/1.1 200 OK\r\n"
"Content-Length: 12\r\n\r\n"),
MockRead("Test Content")};
StaticSocketDataProvider socket_data(reads, writes);
socket_factory_.AddSocketDataProvider(&socket_data);
TestDelegate delegate;
std::unique_ptr<URLRequest> request = context_->CreateRequest(
GURL("http://www.example.com"), DEFAULT_PRIORITY, &delegate,
TRAFFIC_ANNOTATION_FOR_TESTS);
request->set_isolation_info(kTestIsolationInfo);
request->Start();
ASSERT_TRUE(request->is_pending());
delegate.RunUntilComplete();
EXPECT_THAT(delegate.request_status(), IsOk());
EXPECT_EQ(12, request->received_response_content_length());
EXPECT_EQ(CountWriteBytes(writes), request->GetTotalSentBytes());
EXPECT_EQ(CountReadBytes(reads), request->GetTotalReceivedBytes());
}
// Send a HEAD request for the cached response.
{
MockWrite writes[] = {MockWrite(kSimpleHeadMockWrite)};
MockRead reads[] = {
MockRead("HTTP/1.1 200 OK\r\n"
"Content-Length: 0\r\n\r\n")};
StaticSocketDataProvider socket_data(reads, writes);
socket_factory_.AddSocketDataProvider(&socket_data);
TestDelegate delegate;
std::unique_ptr<URLRequest> request = context_->CreateRequest(
GURL("http://www.example.com"), DEFAULT_PRIORITY, &delegate,
TRAFFIC_ANNOTATION_FOR_TESTS);
// Use the cached version.
request->SetLoadFlags(LOAD_SKIP_CACHE_VALIDATION);
request->set_method("HEAD");
request->set_isolation_info(kTestIsolationInfo);
request->Start();
ASSERT_TRUE(request->is_pending());
delegate.RunUntilComplete();
EXPECT_THAT(delegate.request_status(), IsOk());
EXPECT_EQ(0, request->received_response_content_length());
EXPECT_EQ(0, request->GetTotalSentBytes());
EXPECT_EQ(0, request->GetTotalReceivedBytes());
}
}
TEST_F(URLRequestHttpJobWithMockSocketsTest,
TestContentLengthSuccessfulHttp09Request) {
MockWrite writes[] = {MockWrite(kSimpleGetMockWrite)};
MockRead reads[] = {MockRead("Test Content"),
MockRead(net::SYNCHRONOUS, net::OK)};
StaticSocketDataProvider socket_data(reads, base::span<MockWrite>());
socket_factory_.AddSocketDataProvider(&socket_data);
TestDelegate delegate;
std::unique_ptr<URLRequest> request =
context_->CreateRequest(GURL("http://www.example.com"), DEFAULT_PRIORITY,
&delegate, TRAFFIC_ANNOTATION_FOR_TESTS);
request->Start();
ASSERT_TRUE(request->is_pending());
delegate.RunUntilComplete();
EXPECT_THAT(delegate.request_status(), IsOk());
EXPECT_EQ(12, request->received_response_content_length());
EXPECT_EQ(CountWriteBytes(writes), request->GetTotalSentBytes());
EXPECT_EQ(CountReadBytes(reads), request->GetTotalReceivedBytes());
}
TEST_F(URLRequestHttpJobWithMockSocketsTest, TestContentLengthFailedRequest) {
MockWrite writes[] = {MockWrite(kSimpleGetMockWrite)};
MockRead reads[] = {MockRead("HTTP/1.1 200 OK\r\n"
"Content-Length: 20\r\n\r\n"),
MockRead("Test Content"),
MockRead(net::SYNCHRONOUS, net::ERR_FAILED)};
StaticSocketDataProvider socket_data(reads, writes);
socket_factory_.AddSocketDataProvider(&socket_data);
TestDelegate delegate;
std::unique_ptr<URLRequest> request =
context_->CreateRequest(GURL("http://www.example.com"), DEFAULT_PRIORITY,
&delegate, TRAFFIC_ANNOTATION_FOR_TESTS);
request->Start();
ASSERT_TRUE(request->is_pending());
delegate.RunUntilComplete();
EXPECT_THAT(delegate.request_status(), IsError(ERR_FAILED));
EXPECT_EQ(12, request->received_response_content_length());
EXPECT_EQ(CountWriteBytes(writes), request->GetTotalSentBytes());
EXPECT_EQ(CountReadBytes(reads), request->GetTotalReceivedBytes());
}
TEST_F(URLRequestHttpJobWithMockSocketsTest,
TestContentLengthCancelledRequest) {
MockWrite writes[] = {MockWrite(kSimpleGetMockWrite)};
MockRead reads[] = {MockRead("HTTP/1.1 200 OK\r\n"
"Content-Length: 20\r\n\r\n"),
MockRead("Test Content"),
MockRead(net::SYNCHRONOUS, net::ERR_IO_PENDING)};
StaticSocketDataProvider socket_data(reads, writes);
socket_factory_.AddSocketDataProvider(&socket_data);
TestDelegate delegate;
std::unique_ptr<URLRequest> request =
context_->CreateRequest(GURL("http://www.example.com"), DEFAULT_PRIORITY,
&delegate, TRAFFIC_ANNOTATION_FOR_TESTS);
delegate.set_cancel_in_received_data(true);
request->Start();
delegate.RunUntilComplete();
EXPECT_THAT(delegate.request_status(), IsError(ERR_ABORTED));
EXPECT_EQ(12, request->received_response_content_length());
EXPECT_EQ(CountWriteBytes(writes), request->GetTotalSentBytes());
EXPECT_EQ(CountReadBytes(reads), request->GetTotalReceivedBytes());
}
TEST_F(URLRequestHttpJobWithMockSocketsTest,
TestNetworkBytesRedirectedRequest) {
MockWrite redirect_writes[] = {
MockWrite("GET / HTTP/1.1\r\n"
"Host: www.redirect.com\r\n"
"Connection: keep-alive\r\n"
"User-Agent: \r\n"
"Accept-Encoding: gzip, deflate\r\n"
"Accept-Language: en-us,fr\r\n\r\n")};
MockRead redirect_reads[] = {
MockRead("HTTP/1.1 302 Found\r\n"
"Location: http://www.example.com\r\n\r\n"),
};
StaticSocketDataProvider redirect_socket_data(redirect_reads,
redirect_writes);
socket_factory_.AddSocketDataProvider(&redirect_socket_data);
MockWrite final_writes[] = {MockWrite(kSimpleGetMockWrite)};
MockRead final_reads[] = {MockRead("HTTP/1.1 200 OK\r\n"
"Content-Length: 12\r\n\r\n"),
MockRead("Test Content")};
StaticSocketDataProvider final_socket_data(final_reads, final_writes);
socket_factory_.AddSocketDataProvider(&final_socket_data);
TestDelegate delegate;
std::unique_ptr<URLRequest> request =
context_->CreateRequest(GURL("http://www.redirect.com"), DEFAULT_PRIORITY,
&delegate, TRAFFIC_ANNOTATION_FOR_TESTS);
request->Start();
ASSERT_TRUE(request->is_pending());
delegate.RunUntilComplete();
EXPECT_THAT(delegate.request_status(), IsOk());
EXPECT_EQ(12, request->received_response_content_length());
// Should not include the redirect.
EXPECT_EQ(CountWriteBytes(final_writes), request->GetTotalSentBytes());
EXPECT_EQ(CountReadBytes(final_reads), request->GetTotalReceivedBytes());
}
TEST_F(URLRequestHttpJobWithMockSocketsTest,
TestNetworkBytesCancelledAfterHeaders) {
MockWrite writes[] = {MockWrite(kSimpleGetMockWrite)};
MockRead reads[] = {MockRead("HTTP/1.1 200 OK\r\n\r\n")};
StaticSocketDataProvider socket_data(reads, writes);
socket_factory_.AddSocketDataProvider(&socket_data);
TestDelegate delegate;
std::unique_ptr<URLRequest> request =
context_->CreateRequest(GURL("http://www.example.com"), DEFAULT_PRIORITY,
&delegate, TRAFFIC_ANNOTATION_FOR_TESTS);
delegate.set_cancel_in_response_started(true);
request->Start();
delegate.RunUntilComplete();
EXPECT_THAT(delegate.request_status(), IsError(ERR_ABORTED));
EXPECT_EQ(0, request->received_response_content_length());
EXPECT_EQ(CountWriteBytes(writes), request->GetTotalSentBytes());
EXPECT_EQ(CountReadBytes(reads), request->GetTotalReceivedBytes());
}
TEST_F(URLRequestHttpJobWithMockSocketsTest,
TestNetworkBytesCancelledImmediately) {
StaticSocketDataProvider socket_data;
socket_factory_.AddSocketDataProvider(&socket_data);
TestDelegate delegate;
std::unique_ptr<URLRequest> request =
context_->CreateRequest(GURL("http://www.example.com"), DEFAULT_PRIORITY,
&delegate, TRAFFIC_ANNOTATION_FOR_TESTS);
request->Start();
request->Cancel();
delegate.RunUntilComplete();
EXPECT_THAT(delegate.request_status(), IsError(ERR_ABORTED));
EXPECT_EQ(0, request->received_response_content_length());
EXPECT_EQ(0, request->GetTotalSentBytes());
EXPECT_EQ(0, request->GetTotalReceivedBytes());
}
TEST_F(URLRequestHttpJobWithMockSocketsTest, TestHttpTimeToFirstByte) {
base::HistogramTester histograms;
MockWrite writes[] = {MockWrite(kSimpleGetMockWrite)};
MockRead reads[] = {MockRead("HTTP/1.1 200 OK\r\n"
"Content-Length: 12\r\n\r\n"),
MockRead("Test Content")};
StaticSocketDataProvider socket_data(reads, writes);
socket_factory_.AddSocketDataProvider(&socket_data);
TestDelegate delegate;
std::unique_ptr<URLRequest> request =
context_->CreateRequest(GURL("http://www.example.com"), DEFAULT_PRIORITY,
&delegate, TRAFFIC_ANNOTATION_FOR_TESTS);
histograms.ExpectTotalCount("Net.HttpTimeToFirstByte", 0);
request->Start();
delegate.RunUntilComplete();
EXPECT_THAT(delegate.request_status(), IsOk());
histograms.ExpectTotalCount("Net.HttpTimeToFirstByte", 1);
}
TEST_F(URLRequestHttpJobWithMockSocketsTest,
TestHttpTimeToFirstByteForCancelledTask) {
base::HistogramTester histograms;
MockWrite writes[] = {MockWrite(kSimpleGetMockWrite)};
MockRead reads[] = {MockRead("HTTP/1.1 200 OK\r\n"
"Content-Length: 12\r\n\r\n"),
MockRead("Test Content")};
StaticSocketDataProvider socket_data(reads, writes);
socket_factory_.AddSocketDataProvider(&socket_data);
TestDelegate delegate;
std::unique_ptr<URLRequest> request =
context_->CreateRequest(GURL("http://www.example.com"), DEFAULT_PRIORITY,
&delegate, TRAFFIC_ANNOTATION_FOR_TESTS);
request->Start();
request->Cancel();
delegate.RunUntilComplete();
EXPECT_THAT(delegate.request_status(), IsError(ERR_ABORTED));
histograms.ExpectTotalCount("Net.HttpTimeToFirstByte", 0);
}
TEST_F(URLRequestHttpJobWithMockSocketsTest,
TestHttpJobSuccessPriorityKeyedTotalTime) {
base::HistogramTester histograms;
for (int priority = 0; priority < net::NUM_PRIORITIES; ++priority) {
for (int request_index = 0; request_index <= priority; ++request_index) {
MockWrite writes[] = {MockWrite(kSimpleGetMockWrite)};
MockRead reads[] = {MockRead("HTTP/1.1 200 OK\r\n"
"Content-Length: 12\r\n\r\n"),
MockRead("Test Content")};
StaticSocketDataProvider socket_data(reads, writes);
socket_factory_.AddSocketDataProvider(&socket_data);
TestDelegate delegate;
std::unique_ptr<URLRequest> request =
context_->CreateRequest(GURL("http://www.example.com/"),
static_cast<net::RequestPriority>(priority),
&delegate, TRAFFIC_ANNOTATION_FOR_TESTS);
request->Start();
delegate.RunUntilComplete();
EXPECT_THAT(delegate.request_status(), IsOk());
}
}
for (int priority = 0; priority < net::NUM_PRIORITIES; ++priority) {
histograms.ExpectTotalCount("Net.HttpJob.TotalTimeSuccess.Priority" +
base::NumberToString(priority),
priority + 1);
}
}
TEST_F(URLRequestHttpJobWithMockSocketsTest,
TestHttpJobRecordsTrustAnchorHistograms) {
SSLSocketDataProvider ssl_socket_data(net::ASYNC, net::OK);
ssl_socket_data.ssl_info.cert =
ImportCertFromFile(GetTestCertsDirectory(), "ok_cert.pem");
// Simulate a certificate chain issued by "C=US, O=Google Trust Services LLC,
// CN=GTS Root R4". This publicly-trusted root was chosen as it was included
// in 2017 and is not anticipated to be removed from all supported platforms
// for a few decades.
// Note: The actual cert in |cert| does not matter for this testing.
SHA256HashValue leaf_hash = {{0}};
SHA256HashValue intermediate_hash = {{1}};
SHA256HashValue root_hash = {
{0x98, 0x47, 0xe5, 0x65, 0x3e, 0x5e, 0x9e, 0x84, 0x75, 0x16, 0xe5,
0xcb, 0x81, 0x86, 0x06, 0xaa, 0x75, 0x44, 0xa1, 0x9b, 0xe6, 0x7f,
0xd7, 0x36, 0x6d, 0x50, 0x69, 0x88, 0xe8, 0xd8, 0x43, 0x47}};
ssl_socket_data.ssl_info.public_key_hashes.push_back(HashValue(leaf_hash));
ssl_socket_data.ssl_info.public_key_hashes.push_back(
HashValue(intermediate_hash));
ssl_socket_data.ssl_info.public_key_hashes.push_back(HashValue(root_hash));
const base::HistogramBase::Sample kGTSRootR4HistogramID = 486;
socket_factory_.AddSSLSocketDataProvider(&ssl_socket_data);
MockWrite writes[] = {MockWrite(kSimpleGetMockWrite)};
MockRead reads[] = {MockRead("HTTP/1.1 200 OK\r\n"
"Content-Length: 12\r\n\r\n"),
MockRead("Test Content")};
StaticSocketDataProvider socket_data(reads, writes);
socket_factory_.AddSocketDataProvider(&socket_data);
base::HistogramTester histograms;
histograms.ExpectTotalCount(kTrustAnchorRequestHistogram, 0);
TestDelegate delegate;
std::unique_ptr<URLRequest> request = context_->CreateRequest(
GURL("https://www.example.com/"), DEFAULT_PRIORITY, &delegate,
TRAFFIC_ANNOTATION_FOR_TESTS);
request->Start();
delegate.RunUntilComplete();
EXPECT_THAT(delegate.request_status(), IsOk());
histograms.ExpectTotalCount(kTrustAnchorRequestHistogram, 1);
histograms.ExpectUniqueSample(kTrustAnchorRequestHistogram,
kGTSRootR4HistogramID, 1);
}
TEST_F(URLRequestHttpJobWithMockSocketsTest,
TestHttpJobDoesNotRecordTrustAnchorHistogramsWhenNoNetworkLoad) {
SSLSocketDataProvider ssl_socket_data(net::ASYNC, net::OK);
ssl_socket_data.ssl_info.cert =
ImportCertFromFile(GetTestCertsDirectory(), "ok_cert.pem");
// Simulate a request loaded from a non-network source, such as a disk
// cache.
ssl_socket_data.ssl_info.public_key_hashes.clear();
socket_factory_.AddSSLSocketDataProvider(&ssl_socket_data);
MockWrite writes[] = {MockWrite(kSimpleGetMockWrite)};
MockRead reads[] = {MockRead("HTTP/1.1 200 OK\r\n"
"Content-Length: 12\r\n\r\n"),
MockRead("Test Content")};
StaticSocketDataProvider socket_data(reads, writes);
socket_factory_.AddSocketDataProvider(&socket_data);
base::HistogramTester histograms;
histograms.ExpectTotalCount(kTrustAnchorRequestHistogram, 0);
TestDelegate delegate;
std::unique_ptr<URLRequest> request = context_->CreateRequest(
GURL("https://www.example.com/"), DEFAULT_PRIORITY, &delegate,
TRAFFIC_ANNOTATION_FOR_TESTS);
request->Start();
delegate.RunUntilComplete();
EXPECT_THAT(delegate.request_status(), IsOk());
histograms.ExpectTotalCount(kTrustAnchorRequestHistogram, 0);
}
TEST_F(URLRequestHttpJobWithMockSocketsTest,
TestHttpJobRecordsMostSpecificTrustAnchorHistograms) {
SSLSocketDataProvider ssl_socket_data(net::ASYNC, net::OK);
ssl_socket_data.ssl_info.cert =
ImportCertFromFile(GetTestCertsDirectory(), "ok_cert.pem");
// Simulate a certificate chain issued by "C=US, O=Google Trust Services LLC,
// CN=GTS Root R4". This publicly-trusted root was chosen as it was included
// in 2017 and is not anticipated to be removed from all supported platforms
// for a few decades.
// Note: The actual cert in |cert| does not matter for this testing.
SHA256HashValue leaf_hash = {{0}};
SHA256HashValue intermediate_hash = {{1}};
SHA256HashValue gts_root_r3_hash = {
{0x41, 0x79, 0xed, 0xd9, 0x81, 0xef, 0x74, 0x74, 0x77, 0xb4, 0x96,
0x26, 0x40, 0x8a, 0xf4, 0x3d, 0xaa, 0x2c, 0xa7, 0xab, 0x7f, 0x9e,
0x08, 0x2c, 0x10, 0x60, 0xf8, 0x40, 0x96, 0x77, 0x43, 0x48}};
SHA256HashValue gts_root_r4_hash = {
{0x98, 0x47, 0xe5, 0x65, 0x3e, 0x5e, 0x9e, 0x84, 0x75, 0x16, 0xe5,
0xcb, 0x81, 0x86, 0x06, 0xaa, 0x75, 0x44, 0xa1, 0x9b, 0xe6, 0x7f,
0xd7, 0x36, 0x6d, 0x50, 0x69, 0x88, 0xe8, 0xd8, 0x43, 0x47}};
ssl_socket_data.ssl_info.public_key_hashes.push_back(HashValue(leaf_hash));
ssl_socket_data.ssl_info.public_key_hashes.push_back(
HashValue(intermediate_hash));
ssl_socket_data.ssl_info.public_key_hashes.push_back(
HashValue(gts_root_r3_hash));
ssl_socket_data.ssl_info.public_key_hashes.push_back(
HashValue(gts_root_r4_hash));
const base::HistogramBase::Sample kGTSRootR3HistogramID = 485;
socket_factory_.AddSSLSocketDataProvider(&ssl_socket_data);
MockWrite writes[] = {MockWrite(kSimpleGetMockWrite)};
MockRead reads[] = {MockRead("HTTP/1.1 200 OK\r\n"
"Content-Length: 12\r\n\r\n"),
MockRead("Test Content")};
StaticSocketDataProvider socket_data(reads, writes);
socket_factory_.AddSocketDataProvider(&socket_data);
base::HistogramTester histograms;
histograms.ExpectTotalCount(kTrustAnchorRequestHistogram, 0);
TestDelegate delegate;
std::unique_ptr<URLRequest> request = context_->CreateRequest(
GURL("https://www.example.com/"), DEFAULT_PRIORITY, &delegate,
TRAFFIC_ANNOTATION_FOR_TESTS);
request->Start();
delegate.RunUntilComplete();
EXPECT_THAT(delegate.request_status(), IsOk());
histograms.ExpectTotalCount(kTrustAnchorRequestHistogram, 1);
histograms.ExpectUniqueSample(kTrustAnchorRequestHistogram,
kGTSRootR3HistogramID, 1);
}
TEST_F(URLRequestHttpJobWithMockSocketsTest, EncodingAdvertisementOnRange) {
MockWrite writes[] = {
MockWrite("GET / HTTP/1.1\r\n"
"Host: www.example.com\r\n"
"Connection: keep-alive\r\n"
"User-Agent: \r\n"
"Accept-Encoding: identity\r\n"
"Accept-Language: en-us,fr\r\n"
"Range: bytes=0-1023\r\n\r\n")};
MockRead reads[] = {MockRead("HTTP/1.1 200 OK\r\n"
"Accept-Ranges: bytes\r\n"
"Content-Length: 12\r\n\r\n"),
MockRead("Test Content")};
StaticSocketDataProvider socket_data(reads, writes);
socket_factory_.AddSocketDataProvider(&socket_data);
TestDelegate delegate;
std::unique_ptr<URLRequest> request =
context_->CreateRequest(GURL("http://www.example.com"), DEFAULT_PRIORITY,
&delegate, TRAFFIC_ANNOTATION_FOR_TESTS);
// Make the extra header to trigger the change in "Accepted-Encoding"
HttpRequestHeaders headers;
headers.SetHeader("Range", "bytes=0-1023");
request->SetExtraRequestHeaders(headers);
request->Start();
delegate.RunUntilComplete();
EXPECT_THAT(delegate.request_status(), IsOk());
EXPECT_EQ(12, request->received_response_content_length());
EXPECT_EQ(CountWriteBytes(writes), request->GetTotalSentBytes());
EXPECT_EQ(CountReadBytes(reads), request->GetTotalReceivedBytes());
}
TEST_F(URLRequestHttpJobWithMockSocketsTest, RangeRequestOverrideEncoding) {
MockWrite writes[] = {
MockWrite("GET / HTTP/1.1\r\n"
"Host: www.example.com\r\n"
"Connection: keep-alive\r\n"
"Accept-Encoding: gzip, deflate\r\n"
"User-Agent: \r\n"
"Accept-Language: en-us,fr\r\n"
"Range: bytes=0-1023\r\n\r\n")};
MockRead reads[] = {MockRead("HTTP/1.1 200 OK\r\n"
"Accept-Ranges: bytes\r\n"
"Content-Length: 12\r\n\r\n"),
MockRead("Test Content")};
StaticSocketDataProvider socket_data(reads, writes);
socket_factory_.AddSocketDataProvider(&socket_data);
TestDelegate delegate;
std::unique_ptr<URLRequest> request =
context_->CreateRequest(GURL("http://www.example.com"), DEFAULT_PRIORITY,
&delegate, TRAFFIC_ANNOTATION_FOR_TESTS);
// Explicitly set "Accept-Encoding" to make sure it's not overridden by
// AddExtraHeaders
HttpRequestHeaders headers;
headers.SetHeader("Accept-Encoding", "gzip, deflate");
headers.SetHeader("Range", "bytes=0-1023");
request->SetExtraRequestHeaders(headers);
request->Start();
delegate.RunUntilComplete();
EXPECT_THAT(delegate.request_status(), IsOk());
EXPECT_EQ(12, request->received_response_content_length());
EXPECT_EQ(CountWriteBytes(writes), request->GetTotalSentBytes());
EXPECT_EQ(CountReadBytes(reads), request->GetTotalReceivedBytes());
}
TEST_F(URLRequestHttpJobTest, TestCancelWhileReadingCookies) {
auto context_builder = CreateTestURLRequestContextBuilder();
context_builder->SetCookieStore(std::make_unique<DelayedCookieMonster>());
auto context = context_builder->Build();
TestDelegate delegate;
std::unique_ptr<URLRequest> request =
context->CreateRequest(GURL("http://www.example.com"), DEFAULT_PRIORITY,
&delegate, TRAFFIC_ANNOTATION_FOR_TESTS);
request->Start();
request->Cancel();
delegate.RunUntilComplete();
EXPECT_THAT(delegate.request_status(), IsError(ERR_ABORTED));
}
// Make sure that SetPriority actually sets the URLRequestHttpJob's
// priority, before start. Other tests handle the after start case.
TEST_F(URLRequestHttpJobTest, SetPriorityBasic) {
auto job = std::make_unique<TestURLRequestHttpJob>(req_.get());
EXPECT_EQ(DEFAULT_PRIORITY, job->priority());
job->SetPriority(LOWEST);
EXPECT_EQ(LOWEST, job->priority());
job->SetPriority(LOW);
EXPECT_EQ(LOW, job->priority());
}
// Make sure that URLRequestHttpJob passes on its priority to its
// transaction on start.
TEST_F(URLRequestHttpJobTest, SetTransactionPriorityOnStart) {
TestScopedURLInterceptor interceptor(
req_->url(), std::make_unique<TestURLRequestHttpJob>(req_.get()));
req_->SetPriority(LOW);
EXPECT_FALSE(network_layer().last_transaction());
req_->Start();
ASSERT_TRUE(network_layer().last_transaction());
EXPECT_EQ(LOW, network_layer().last_transaction()->priority());
}
// Make sure that URLRequestHttpJob passes on its priority updates to
// its transaction.
TEST_F(URLRequestHttpJobTest, SetTransactionPriority) {
TestScopedURLInterceptor interceptor(
req_->url(), std::make_unique<TestURLRequestHttpJob>(req_.get()));
req_->SetPriority(LOW);
req_->Start();
ASSERT_TRUE(network_layer().last_transaction());
EXPECT_EQ(LOW, network_layer().last_transaction()->priority());
req_->SetPriority(HIGHEST);
EXPECT_EQ(HIGHEST, network_layer().last_transaction()->priority());
}
TEST_F(URLRequestHttpJobTest, HSTSInternalRedirectTest) {
// Setup HSTS state.
context_->transport_security_state()->AddHSTS(
"upgrade.test", base::Time::Now() + base::Seconds(10), true);
ASSERT_TRUE(
context_->transport_security_state()->ShouldUpgradeToSSL("upgrade.test"));
ASSERT_FALSE(context_->transport_security_state()->ShouldUpgradeToSSL(
"no-upgrade.test"));
struct TestCase {
const char* url;
bool upgrade_expected;
const char* url_expected;
} cases[] = {
{"http://upgrade.test/", true, "https://upgrade.test/"},
{"http://upgrade.test:123/", true, "https://upgrade.test:123/"},
{"http://no-upgrade.test/", false, "http://no-upgrade.test/"},
{"http://no-upgrade.test:123/", false, "http://no-upgrade.test:123/"},
#if BUILDFLAG(ENABLE_WEBSOCKETS)
{"ws://upgrade.test/", true, "wss://upgrade.test/"},
{"ws://upgrade.test:123/", true, "wss://upgrade.test:123/"},
{"ws://no-upgrade.test/", false, "ws://no-upgrade.test/"},
{"ws://no-upgrade.test:123/", false, "ws://no-upgrade.test:123/"},
#endif // BUILDFLAG(ENABLE_WEBSOCKETS)
};
for (const auto& test : cases) {
SCOPED_TRACE(test.url);
GURL url = GURL(test.url);
// This is needed to bypass logic that rejects using URLRequests directly
// for WebSocket requests.
bool is_for_websockets = url.SchemeIsWSOrWSS();
TestDelegate d;
TestNetworkDelegate network_delegate;
std::unique_ptr<URLRequest> r(context_->CreateRequest(
url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS,
is_for_websockets));
net_log_observer_.Clear();
r->Start();
d.RunUntilComplete();
if (test.upgrade_expected) {
auto entries = net_log_observer_.GetEntriesWithType(
net::NetLogEventType::URL_REQUEST_REDIRECT_JOB);
int redirects = entries.size();
for (const auto& entry : entries) {
EXPECT_EQ("HSTS", GetStringValueFromParams(entry, "reason"));
}
EXPECT_EQ(1, redirects);
EXPECT_EQ(1, d.received_redirect_count());
EXPECT_EQ(2u, r->url_chain().size());
} else {
EXPECT_EQ(0, d.received_redirect_count());
EXPECT_EQ(1u, r->url_chain().size());
}
EXPECT_EQ(GURL(test.url_expected), r->url());
}
}
TEST_F(URLRequestHttpJobTest, ShouldBypassHSTS) {
// Setup HSTS state.
context_->transport_security_state()->AddHSTS(
"upgrade.test", base::Time::Now() + base::Seconds(30), true);
ASSERT_TRUE(
context_->transport_security_state()->ShouldUpgradeToSSL("upgrade.test"));
struct TestCase {
const char* url;
bool bypass_hsts;
const char* url_expected;
} cases[] = {
{"http://upgrade.test/example.crl", true,
"http://upgrade.test/example.crl"},
// This test ensures that the HSTS check and upgrade happens prior to cache
// and socket pool checks
{"http://upgrade.test/example.crl", false,
"https://upgrade.test/example.crl"},
{"http://upgrade.test", false, "https://upgrade.test"},
{"http://upgrade.test:1080", false, "https://upgrade.test:1080"},
#if BUILDFLAG(ENABLE_WEBSOCKETS)
{"ws://upgrade.test/example.crl", true, "ws://upgrade.test/example.crl"},
{"ws://upgrade.test/example.crl", false, "wss://upgrade.test/example.crl"},
{"ws://upgrade.test", false, "wss://upgrade.test"},
{"ws://upgrade.test:1080", false, "wss://upgrade.test:1080"},
#endif // BUILDFLAG(ENABLE_WEBSOCKETS)
};
for (const auto& test : cases) {
SCOPED_TRACE(test.url);
GURL url = GURL(test.url);
// This is needed to bypass logic that rejects using URLRequests directly
// for WebSocket requests.
bool is_for_websockets = url.SchemeIsWSOrWSS();
TestDelegate d;
TestNetworkDelegate network_delegate;
std::unique_ptr<URLRequest> r(context_->CreateRequest(
url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS,
is_for_websockets));
if (test.bypass_hsts) {
r->SetLoadFlags(net::LOAD_SHOULD_BYPASS_HSTS);
r->set_allow_credentials(false);
}
net_log_observer_.Clear();
r->Start();
d.RunUntilComplete();
if (test.bypass_hsts) {
EXPECT_EQ(0, d.received_redirect_count());
EXPECT_EQ(1u, r->url_chain().size());
} else {
auto entries = net_log_observer_.GetEntriesWithType(
net::NetLogEventType::URL_REQUEST_REDIRECT_JOB);
int redirects = entries.size();
for (const auto& entry : entries) {
EXPECT_EQ("HSTS", GetStringValueFromParams(entry, "reason"));
}
EXPECT_EQ(1, redirects);
EXPECT_EQ(1, d.received_redirect_count());
EXPECT_EQ(2u, r->url_chain().size());
}
EXPECT_EQ(GURL(test.url_expected), r->url());
}
}
namespace {
std::unique_ptr<test_server::HttpResponse> HandleRequest(
const std::string_view& content,
const test_server::HttpRequest& request) {
auto response = std::make_unique<test_server::BasicHttpResponse>();
response->set_content(content);
return std::move(response);
}
} // namespace
// This test checks that if an HTTP connection was made for a request that has
// the should_bypass_hsts flag set to true, subsequent calls to the exact same
// URL WITHOUT should_bypass_hsts=true will be upgraded to HTTPS early
// enough in the process such that the HTTP socket connection is not re-used,
// and the request does not have a hit in the cache.
TEST_F(URLRequestHttpJobTest, ShouldBypassHSTSResponseAndConnectionNotReused) {
constexpr std::string_view kSecureContent = "Secure: Okay Content";
constexpr std::string_view kInsecureContent = "Insecure: Bad Content";
auto context_builder = CreateTestURLRequestContextBuilder();
auto context = context_builder->Build();
// The host of all EmbeddedTestServer URLs is 127.0.0.1.
context->transport_security_state()->AddHSTS(
"127.0.0.1", base::Time::Now() + base::Seconds(30), true);
ASSERT_TRUE(
context->transport_security_state()->ShouldUpgradeToSSL("127.0.0.1"));
GURL::Replacements replace_scheme;
replace_scheme.SetSchemeStr("https");
GURL insecure_url;
GURL secure_url;
int common_port = 0;
// Create an HTTP request that is not upgraded to the should_bypass_hsts flag,
// and ensure that the response is stored in the cache.
{
EmbeddedTestServer http_server(EmbeddedTestServer::TYPE_HTTP);
http_server.AddDefaultHandlers(base::FilePath());
http_server.RegisterRequestHandler(
base::BindRepeating(&HandleRequest, kInsecureContent));
ASSERT_TRUE(http_server.Start());
common_port = http_server.port();
insecure_url = http_server.base_url();
ASSERT_TRUE(insecure_url.SchemeIs("http"));
secure_url = insecure_url.ReplaceComponents(replace_scheme);
ASSERT_TRUE(secure_url.SchemeIs("https"));
net_log_observer_.Clear();
TestDelegate delegate;
std::unique_ptr<URLRequest> req(
context->CreateRequest(insecure_url, DEFAULT_PRIORITY, &delegate,
TRAFFIC_ANNOTATION_FOR_TESTS));
req->SetLoadFlags(net::LOAD_SHOULD_BYPASS_HSTS);
req->set_allow_credentials(false);
req->Start();
delegate.RunUntilComplete();
EXPECT_EQ(kInsecureContent, delegate.data_received());
// There should be 2 cache event entries, one for beginning the read and one
// for finishing the read.
EXPECT_EQ(2u, net_log_observer_
.GetEntriesWithType(
net::NetLogEventType::HTTP_CACHE_ADD_TO_ENTRY)
.size());
ASSERT_TRUE(http_server.ShutdownAndWaitUntilComplete());
}
// Test that a request with the same URL will be upgraded as long as
// should_bypass_hsts flag is not set, and doesn't have an cache hit or
// re-use an existing socket connection.
{
EmbeddedTestServer https_server(EmbeddedTestServer::TYPE_HTTPS);
https_server.AddDefaultHandlers(base::FilePath());
https_server.RegisterRequestHandler(
base::BindRepeating(&HandleRequest, kSecureContent));
ASSERT_TRUE(https_server.Start(common_port));
TestDelegate delegate;
std::unique_ptr<URLRequest> req(
context->CreateRequest(insecure_url, DEFAULT_PRIORITY, &delegate,
TRAFFIC_ANNOTATION_FOR_TESTS));
req->set_allow_credentials(false);
req->Start();
delegate.RunUntilRedirect();
// Ensure that the new URL has an upgraded protocol. This ensures that when
// the redirect request continues, the HTTP socket connection from before
// will not be re-used, given that "protocol" is one of the fields used to
// create a socket connection. Documentation here:
// https://chromium.googlesource.com/chromium/src/+/HEAD/net/docs/life-of-a-url-request.md
// under "Socket Pools" section.
EXPECT_EQ(delegate.redirect_info().new_url, secure_url);
EXPECT_TRUE(delegate.redirect_info().new_url.SchemeIs("https"));
EXPECT_THAT(delegate.request_status(), net::ERR_IO_PENDING);
req->FollowDeferredRedirect(std::nullopt /* removed_headers */,
std::nullopt /* modified_headers */);
delegate.RunUntilComplete();
EXPECT_EQ(kSecureContent, delegate.data_received());
EXPECT_FALSE(req->was_cached());
ASSERT_TRUE(https_server.ShutdownAndWaitUntilComplete());
}
}
TEST_F(URLRequestHttpJobTest, HSTSInternalRedirectCallback) {
EmbeddedTestServer https_test(EmbeddedTestServer::TYPE_HTTPS);
https_test.AddDefaultHandlers(base::FilePath());
ASSERT_TRUE(https_test.Start());
auto context = CreateTestURLRequestContextBuilder()->Build();
context->transport_security_state()->AddHSTS(
"127.0.0.1", base::Time::Now() + base::Seconds(10), true);
ASSERT_TRUE(
context->transport_security_state()->ShouldUpgradeToSSL("127.0.0.1"));
GURL::Replacements replace_scheme;
replace_scheme.SetSchemeStr("http");
{
GURL url(
https_test.GetURL("/echoheader").ReplaceComponents(replace_scheme));
TestDelegate delegate;
HttpRequestHeaders extra_headers;
extra_headers.SetHeader("X-HSTS-Test", "1");
HttpRawRequestHeaders raw_req_headers;
std::unique_ptr<URLRequest> r(context->CreateRequest(
url, DEFAULT_PRIORITY, &delegate, TRAFFIC_ANNOTATION_FOR_TESTS));
r->SetExtraRequestHeaders(extra_headers);
r->SetRequestHeadersCallback(base::BindRepeating(
&HttpRawRequestHeaders::Assign, base::Unretained(&raw_req_headers)));
r->Start();
delegate.RunUntilRedirect();
EXPECT_FALSE(raw_req_headers.headers().empty());
std::string value;
EXPECT_TRUE(raw_req_headers.FindHeaderForTest("X-HSTS-Test", &value));
EXPECT_EQ("1", value);
EXPECT_EQ("GET /echoheader HTTP/1.1\r\n", raw_req_headers.request_line());
raw_req_headers = HttpRawRequestHeaders();
r->FollowDeferredRedirect(std::nullopt /* removed_headers */,
std::nullopt /* modified_headers */);
delegate.RunUntilComplete();
EXPECT_FALSE(raw_req_headers.headers().empty());
}
{
GURL url(https_test.GetURL("/echoheader?foo=bar")
.ReplaceComponents(replace_scheme));
TestDelegate delegate;
HttpRawRequestHeaders raw_req_headers;
std::unique_ptr<URLRequest> r(context->CreateRequest(
url, DEFAULT_PRIORITY, &delegate, TRAFFIC_ANNOTATION_FOR_TESTS));
r->SetRequestHeadersCallback(base::BindRepeating(
&HttpRawRequestHeaders::Assign, base::Unretained(&raw_req_headers)));
r->Start();
delegate.RunUntilRedirect();
EXPECT_EQ("GET /echoheader?foo=bar HTTP/1.1\r\n",
raw_req_headers.request_line());
}
{
GURL url(
https_test.GetURL("/echoheader#foo").ReplaceComponents(replace_scheme));
TestDelegate delegate;
HttpRawRequestHeaders raw_req_headers;
std::unique_ptr<URLRequest> r(context->CreateRequest(
url, DEFAULT_PRIORITY, &delegate, TRAFFIC_ANNOTATION_FOR_TESTS));
r->SetRequestHeadersCallback(base::BindRepeating(
&HttpRawRequestHeaders::Assign, base::Unretained(&raw_req_headers)));
r->Start();
delegate.RunUntilRedirect();
EXPECT_EQ("GET /echoheader HTTP/1.1\r\n", raw_req_headers.request_line());
}
}
class URLRequestHttpJobWithBrotliSupportTest : public TestWithTaskEnvironment {
protected:
URLRequestHttpJobWithBrotliSupportTest() {
HttpNetworkSessionParams params;
auto context_builder = CreateTestURLRequestContextBuilder();
context_builder->set_enable_brotli(true);
context_builder->set_http_network_session_params(params);
context_builder->set_client_socket_factory_for_testing(&socket_factory_);
context_ = context_builder->Build();
}
MockClientSocketFactory socket_factory_;
std::unique_ptr<URLRequestContext> context_;
};
TEST_F(URLRequestHttpJobWithBrotliSupportTest, NoBrotliAdvertisementOverHttp) {
MockWrite writes[] = {MockWrite(kSimpleGetMockWrite)};
MockRead reads[] = {MockRead("HTTP/1.1 200 OK\r\n"
"Content-Length: 12\r\n\r\n"),
MockRead("Test Content")};
StaticSocketDataProvider socket_data(reads, writes);
socket_factory_.AddSocketDataProvider(&socket_data);
TestDelegate delegate;
std::unique_ptr<URLRequest> request =
context_->CreateRequest(GURL("http://www.example.com"), DEFAULT_PRIORITY,
&delegate, TRAFFIC_ANNOTATION_FOR_TESTS);
request->Start();
delegate.RunUntilComplete();
EXPECT_THAT(delegate.request_status(), IsOk());
EXPECT_EQ(12, request->received_response_content_length());
EXPECT_EQ(CountWriteBytes(writes), request->GetTotalSentBytes());
EXPECT_EQ(CountReadBytes(reads), request->GetTotalReceivedBytes());
}
TEST_F(URLRequestHttpJobWithBrotliSupportTest, BrotliAdvertisement) {
net::SSLSocketDataProvider ssl_socket_data_provider(net::ASYNC, net::OK);
ssl_socket_data_provider.next_proto = kProtoHTTP11;
ssl_socket_data_provider.ssl_info.cert =
ImportCertFromFile(GetTestCertsDirectory(), "unittest.selfsigned.der");
ASSERT_TRUE(ssl_socket_data_provider.ssl_info.cert);
socket_factory_.AddSSLSocketDataProvider(&ssl_socket_data_provider);
MockWrite writes[] = {
MockWrite("GET / HTTP/1.1\r\n"
"Host: www.example.com\r\n"
"Connection: keep-alive\r\n"
"User-Agent: \r\n"
"Accept-Encoding: gzip, deflate, br\r\n"
"Accept-Language: en-us,fr\r\n\r\n")};
MockRead reads[] = {MockRead("HTTP/1.1 200 OK\r\n"
"Content-Length: 12\r\n\r\n"),
MockRead("Test Content")};
StaticSocketDataProvider socket_data(reads, writes);
socket_factory_.AddSocketDataProvider(&socket_data);
TestDelegate delegate;
std::unique_ptr<URLRequest> request =
context_->CreateRequest(GURL("https://www.example.com"), DEFAULT_PRIORITY,
&delegate, TRAFFIC_ANNOTATION_FOR_TESTS);
request->Start();
delegate.RunUntilComplete();
EXPECT_THAT(delegate.request_status(), IsOk());
EXPECT_EQ(12, request->received_response_content_length());
EXPECT_EQ(CountWriteBytes(writes), request->GetTotalSentBytes());
EXPECT_EQ(CountReadBytes(reads), request->GetTotalReceivedBytes());
}
TEST_F(URLRequestHttpJobWithBrotliSupportTest, DefaultAcceptEncodingOverriden) {
struct {
base::flat_set<net::SourceStream::SourceType> accepted_types;
const char* expected_request_headers;
} kTestCases[] = {{{net::SourceStream::SourceType::TYPE_DEFLATE},
"GET / HTTP/1.1\r\n"
"Host: www.example.com\r\n"
"Connection: keep-alive\r\n"
"User-Agent: \r\n"
"Accept-Encoding: deflate\r\n"
"Accept-Language: en-us,fr\r\n\r\n"},
{{},
"GET / HTTP/1.1\r\n"
"Host: www.example.com\r\n"
"Connection: keep-alive\r\n"
"User-Agent: \r\n"
"Accept-Language: en-us,fr\r\n\r\n"},
{{net::SourceStream::SourceType::TYPE_GZIP},
"GET / HTTP/1.1\r\n"
"Host: www.example.com\r\n"
"Connection: keep-alive\r\n"
"User-Agent: \r\n"
"Accept-Encoding: gzip\r\n"
"Accept-Language: en-us,fr\r\n\r\n"},
{{net::SourceStream::SourceType::TYPE_GZIP,
net::SourceStream::SourceType::TYPE_DEFLATE},
"GET / HTTP/1.1\r\n"
"Host: www.example.com\r\n"
"Connection: keep-alive\r\n"
"User-Agent: \r\n"
"Accept-Encoding: gzip, deflate\r\n"
"Accept-Language: en-us,fr\r\n\r\n"},
{{net::SourceStream::SourceType::TYPE_BROTLI},
"GET / HTTP/1.1\r\n"
"Host: www.example.com\r\n"
"Connection: keep-alive\r\n"
"User-Agent: \r\n"
"Accept-Encoding: br\r\n"
"Accept-Language: en-us,fr\r\n\r\n"},
{{net::SourceStream::SourceType::TYPE_BROTLI,
net::SourceStream::SourceType::TYPE_GZIP,
net::SourceStream::SourceType::TYPE_DEFLATE},
"GET / HTTP/1.1\r\n"
"Host: www.example.com\r\n"
"Connection: keep-alive\r\n"
"User-Agent: \r\n"
"Accept-Encoding: gzip, deflate, br\r\n"
"Accept-Language: en-us,fr\r\n\r\n"}};
for (auto test : kTestCases) {
net::SSLSocketDataProvider ssl_socket_data_provider(net::ASYNC, net::OK);
ssl_socket_data_provider.next_proto = kProtoHTTP11;
ssl_socket_data_provider.ssl_info.cert =
ImportCertFromFile(GetTestCertsDirectory(), "unittest.selfsigned.der");
ASSERT_TRUE(ssl_socket_data_provider.ssl_info.cert);
socket_factory_.AddSSLSocketDataProvider(&ssl_socket_data_provider);
MockWrite writes[] = {MockWrite(test.expected_request_headers)};
MockRead reads[] = {MockRead("HTTP/1.1 200 OK\r\n"
"Content-Length: 12\r\n\r\n"),
MockRead("Test Content")};
StaticSocketDataProvider socket_data(reads, writes);
socket_factory_.AddSocketDataProvider(&socket_data);
TestDelegate delegate;
std::unique_ptr<URLRequest> request = context_->CreateRequest(
GURL("https://www.example.com"), DEFAULT_PRIORITY, &delegate,
TRAFFIC_ANNOTATION_FOR_TESTS);
request->set_accepted_stream_types(test.accepted_types);
request->Start();
delegate.RunUntilComplete();
EXPECT_THAT(delegate.request_status(), IsOk());
socket_factory_.ResetNextMockIndexes();
}
}
#if BUILDFLAG(IS_ANDROID)
class URLRequestHttpJobWithCheckClearTextPermittedTest
: public TestWithTaskEnvironment {
protected:
URLRequestHttpJobWithCheckClearTextPermittedTest() {
auto context_builder = CreateTestURLRequestContextBuilder();
context_builder->SetHttpTransactionFactoryForTesting(
std::make_unique<MockNetworkLayer>());
context_builder->set_check_cleartext_permitted(true);
context_builder->set_client_socket_factory_for_testing(&socket_factory_);
context_ = context_builder->Build();
}
MockClientSocketFactory socket_factory_;
std::unique_ptr<URLRequestContext> context_;
};
TEST_F(URLRequestHttpJobWithCheckClearTextPermittedTest,
AndroidCleartextPermittedTest) {
static constexpr struct TestCase {
const char* url;
bool cleartext_permitted;
bool should_block;
int expected_per_host_call_count;
int expected_default_call_count;
} kTestCases[] = {
{"http://unblocked.test/", true, false, 1, 0},
{"https://unblocked.test/", true, false, 0, 0},
{"http://blocked.test/", false, true, 1, 0},
{"https://blocked.test/", false, false, 0, 0},
// If determining the per-host cleartext policy causes an
// IllegalArgumentException (because the hostname is invalid),
// the default configuration should be applied, and the
// exception should not cause a JNI error.
{"http://./", false, true, 1, 1},
{"http://./", true, false, 1, 1},
// Even if the host name would be considered invalid, https
// schemes should not trigger cleartext policy checks.
{"https://./", false, false, 0, 0},
};
JNIEnv* env = base::android::AttachCurrentThread();
for (const TestCase& test : kTestCases) {
Java_AndroidNetworkLibraryTestUtil_setUpSecurityPolicyForTesting(
env, test.cleartext_permitted);
TestDelegate delegate;
std::unique_ptr<URLRequest> request =
context_->CreateRequest(GURL(test.url), DEFAULT_PRIORITY, &delegate,
TRAFFIC_ANNOTATION_FOR_TESTS);
request->Start();
delegate.RunUntilComplete();
if (test.should_block) {
EXPECT_THAT(delegate.request_status(),
IsError(ERR_CLEARTEXT_NOT_PERMITTED));
} else {
// Should fail since there's no test server running
EXPECT_THAT(delegate.request_status(), IsError(ERR_FAILED));
}
EXPECT_EQ(
Java_AndroidNetworkLibraryTestUtil_getPerHostCleartextCheckCount(env),
test.expected_per_host_call_count);
EXPECT_EQ(
Java_AndroidNetworkLibraryTestUtil_getDefaultCleartextCheckCount(env),
test.expected_default_call_count);
}
}
#endif
#if BUILDFLAG(ENABLE_WEBSOCKETS)
class URLRequestHttpJobWebSocketTest : public TestWithTaskEnvironment {
protected:
URLRequestHttpJobWebSocketTest() {
auto context_builder = CreateTestURLRequestContextBuilder();
context_builder->set_client_socket_factory_for_testing(&socket_factory_);
context_ = context_builder->Build();
req_ =
context_->CreateRequest(GURL("ws://www.example.org"), DEFAULT_PRIORITY,
&delegate_, TRAFFIC_ANNOTATION_FOR_TESTS,
/*is_for_websockets=*/true);
}
std::unique_ptr<URLRequestContext> context_;
MockClientSocketFactory socket_factory_;
TestDelegate delegate_;
std::unique_ptr<URLRequest> req_;
};
TEST_F(URLRequestHttpJobWebSocketTest, RejectedWithoutCreateHelper) {
req_->Start();
delegate_.RunUntilComplete();
EXPECT_THAT(delegate_.request_status(), IsError(ERR_DISALLOWED_URL_SCHEME));
}
TEST_F(URLRequestHttpJobWebSocketTest, CreateHelperPassedThrough) {
HttpRequestHeaders headers;
headers.SetHeader("Connection", "Upgrade");
headers.SetHeader("Upgrade", "websocket");
headers.SetHeader("Origin", "http://www.example.org");
headers.SetHeader("Sec-WebSocket-Version", "13");
req_->SetExtraRequestHeaders(headers);
MockWrite writes[] = {
MockWrite("GET / HTTP/1.1\r\n"
"Host: www.example.org\r\n"
"Connection: Upgrade\r\n"
"Upgrade: websocket\r\n"
"Origin: http://www.example.org\r\n"
"Sec-WebSocket-Version: 13\r\n"
"User-Agent: \r\n"
"Accept-Encoding: gzip, deflate\r\n"
"Accept-Language: en-us,fr\r\n"
"Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
"Sec-WebSocket-Extensions: permessage-deflate; "
"client_max_window_bits\r\n\r\n")};
MockRead reads[] = {
MockRead("HTTP/1.1 101 Switching Protocols\r\n"
"Upgrade: websocket\r\n"
"Connection: Upgrade\r\n"
"Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n\r\n"),
MockRead(ASYNC, 0)};
StaticSocketDataProvider data(reads, writes);
socket_factory_.AddSocketDataProvider(&data);
auto websocket_stream_create_helper =
std::make_unique<TestWebSocketHandshakeStreamCreateHelper>();
req_->SetUserData(kWebSocketHandshakeUserDataKey,
std::move(websocket_stream_create_helper));
req_->SetLoadFlags(LOAD_DISABLE_CACHE);
req_->Start();
delegate_.RunUntilComplete();
EXPECT_THAT(delegate_.request_status(), IsOk());
EXPECT_TRUE(delegate_.response_completed());
EXPECT_TRUE(data.AllWriteDataConsumed());
EXPECT_TRUE(data.AllReadDataConsumed());
}
#endif // BUILDFLAG(ENABLE_WEBSOCKETS)
bool SetAllCookies(CookieMonster* cm, const CookieList& list) {
DCHECK(cm);
ResultSavingCookieCallback<CookieAccessResult> callback;
cm->SetAllCookiesAsync(list, callback.MakeCallback());
callback.WaitUntilDone();
return callback.result().status.IsInclude();
}
bool CreateAndSetCookie(CookieStore* cs,
const GURL& url,
const std::string& cookie_line) {
auto cookie = CanonicalCookie::Create(
url, cookie_line, base::Time::Now(), std::nullopt /* server_time */,
std::nullopt /* cookie_partition_key */);
if (!cookie)
return false;
DCHECK(cs);
ResultSavingCookieCallback<CookieAccessResult> callback;
cs->SetCanonicalCookieAsync(std::move(cookie), url,
CookieOptions::MakeAllInclusive(),
callback.MakeCallback());
callback.WaitUntilDone();
return callback.result().status.IsInclude();
}
void RunRequest(URLRequestContext* context, const GURL& url) {
TestDelegate delegate;
std::unique_ptr<URLRequest> request = context->CreateRequest(
url, DEFAULT_PRIORITY, &delegate, TRAFFIC_ANNOTATION_FOR_TESTS);
// Make this a laxly same-site context to allow setting
// SameSite=Lax-by-default cookies.
request->set_site_for_cookies(SiteForCookies::FromUrl(url));
request->Start();
delegate.RunUntilComplete();
}
} // namespace
TEST_F(URLRequestHttpJobTest, CookieSchemeRequestSchemeHistogram) {
base::HistogramTester histograms;
const std::string test_histogram = "Cookie.CookieSchemeRequestScheme";
auto context_builder = CreateTestURLRequestContextBuilder();
context_builder->SetCookieStore(std::make_unique<CookieMonster>(
/*store=*/nullptr, /*net_log=*/nullptr));
auto context = context_builder->Build();
auto* cookie_store = static_cast<CookieMonster*>(context->cookie_store());
// Secure set cookie marked as Unset source scheme.
// Using port 7 because it fails the transaction without sending a request and
// prevents a timeout due to the fake addresses. Because we only need the
// headers to be generated (and thus the histogram filled) and not actually
// sent this is acceptable.
GURL nonsecure_url_for_unset1("http://unset1.example:7");
GURL secure_url_for_unset1("https://unset1.example:7");
// Normally the source scheme would be set by
// CookieMonster::SetCanonicalCookie(), however we're using SetAllCookies() to
// bypass the source scheme check in order to test the kUnset state which
// would normally only happen during an existing cookie DB version upgrade.
std::unique_ptr<CanonicalCookie> unset_cookie1 = CanonicalCookie::Create(
secure_url_for_unset1, "NoSourceSchemeHttps=val", base::Time::Now(),
std::nullopt /* server_time */, std::nullopt /* cookie_partition_key */);
unset_cookie1->SetSourceScheme(net::CookieSourceScheme::kUnset);
CookieList list1 = {*unset_cookie1};
EXPECT_TRUE(SetAllCookies(cookie_store, list1));
RunRequest(context.get(), nonsecure_url_for_unset1);
histograms.ExpectBucketCount(
test_histogram,
URLRequestHttpJob::CookieRequestScheme::kUnsetCookieScheme, 1);
RunRequest(context.get(), secure_url_for_unset1);
histograms.ExpectBucketCount(
test_histogram,
URLRequestHttpJob::CookieRequestScheme::kUnsetCookieScheme, 2);
// Nonsecure set cookie marked as unset source scheme.
GURL nonsecure_url_for_unset2("http://unset2.example:7");
GURL secure_url_for_unset2("https://unset2.example:7");
std::unique_ptr<CanonicalCookie> unset_cookie2 = CanonicalCookie::Create(
nonsecure_url_for_unset2, "NoSourceSchemeHttp=val", base::Time::Now(),
std::nullopt /* server_time */, std::nullopt /* cookie_partition_key */);
unset_cookie2->SetSourceScheme(net::CookieSourceScheme::kUnset);
CookieList list2 = {*unset_cookie2};
EXPECT_TRUE(SetAllCookies(cookie_store, list2));
RunRequest(context.get(), nonsecure_url_for_unset2);
histograms.ExpectBucketCount(
test_histogram,
URLRequestHttpJob::CookieRequestScheme::kUnsetCookieScheme, 3);
RunRequest(context.get(), secure_url_for_unset2);
histograms.ExpectBucketCount(
test_histogram,
URLRequestHttpJob::CookieRequestScheme::kUnsetCookieScheme, 4);
// Secure set cookie with source scheme marked appropriately.
GURL nonsecure_url_for_secure_set("http://secureset.example:7");
GURL secure_url_for_secure_set("https://secureset.example:7");
EXPECT_TRUE(CreateAndSetCookie(cookie_store, secure_url_for_secure_set,
"SecureScheme=val"));
RunRequest(context.get(), nonsecure_url_for_secure_set);
histograms.ExpectBucketCount(
test_histogram,
URLRequestHttpJob::CookieRequestScheme::kSecureSetNonsecureRequest, 1);
RunRequest(context.get(), secure_url_for_secure_set);
histograms.ExpectBucketCount(
test_histogram,
URLRequestHttpJob::CookieRequestScheme::kSecureSetSecureRequest, 1);
// Nonsecure set cookie with source scheme marked appropriately.
GURL nonsecure_url_for_nonsecure_set("http://nonsecureset.example:7");
GURL secure_url_for_nonsecure_set("https://nonsecureset.example:7");
EXPECT_TRUE(CreateAndSetCookie(cookie_store, nonsecure_url_for_nonsecure_set,
"NonSecureScheme=val"));
RunRequest(context.get(), nonsecure_url_for_nonsecure_set);
histograms.ExpectBucketCount(
test_histogram,
URLRequestHttpJob::CookieRequestScheme::kNonsecureSetNonsecureRequest, 1);
RunRequest(context.get(), secure_url_for_nonsecure_set);
histograms.ExpectBucketCount(
test_histogram,
URLRequestHttpJob::CookieRequestScheme::kNonsecureSetSecureRequest, 1);
}
// Test that cookies are annotated with the appropriate exclusion reason when
// privacy mode is enabled.
TEST_F(URLRequestHttpJobTest, PrivacyMode_ExclusionReason) {
HttpTestServer test_server;
ASSERT_TRUE(test_server.Start());
auto context_builder = CreateTestURLRequestContextBuilder();
context_builder->SetCookieStore(std::make_unique<CookieMonster>(
/*store=*/nullptr, /*net_log=*/nullptr));
auto& network_delegate = *context_builder->set_network_delegate(
std::make_unique<FilteringTestNetworkDelegate>());
auto context = context_builder->Build();
// Set cookies.
{
TestDelegate d;
GURL test_url = test_server.GetURL(
"/set-cookie?one=1&"
"two=2&"
"three=3");
std::unique_ptr<URLRequest> req =
CreateFirstPartyRequest(*context, test_url, &d);
req->Start();
d.RunUntilComplete();
}
// Get cookies.
network_delegate.ResetAnnotateCookiesCalledCount();
ASSERT_EQ(0, network_delegate.annotate_cookies_called_count());
// We want to fetch cookies from the cookie store, so we use the
// NetworkDelegate to override the privacy mode (rather than setting it via
// `allow_credentials`, since that skips querying the cookie store).
network_delegate.set_force_privacy_mode(true);
TestDelegate d;
std::unique_ptr<URLRequest> req = CreateFirstPartyRequest(
*context, test_server.GetURL("/echoheader?Cookie"), &d);
req->Start();
d.RunUntilComplete();
EXPECT_EQ("None", d.data_received());
EXPECT_THAT(
req->maybe_sent_cookies(),
UnorderedElementsAre(
MatchesCookieWithAccessResult(
MatchesCookieWithName("one"),
MatchesCookieAccessResult(
HasExactlyExclusionReasonsForTesting(
std::vector<CookieInclusionStatus::ExclusionReason>{
CookieInclusionStatus::EXCLUDE_USER_PREFERENCES}),
_, _, _)),
MatchesCookieWithAccessResult(
MatchesCookieWithName("two"),
MatchesCookieAccessResult(
HasExactlyExclusionReasonsForTesting(
std::vector<CookieInclusionStatus::ExclusionReason>{
CookieInclusionStatus::EXCLUDE_USER_PREFERENCES}),
_, _, _)),
MatchesCookieWithAccessResult(
MatchesCookieWithName("three"),
MatchesCookieAccessResult(
HasExactlyExclusionReasonsForTesting(
std::vector<CookieInclusionStatus::ExclusionReason>{
CookieInclusionStatus::EXCLUDE_USER_PREFERENCES}),
_, _, _))));
EXPECT_EQ(0, network_delegate.annotate_cookies_called_count());
}
// Test that cookies are allowed to be selectively blocked by the network
// delegate.
TEST_F(URLRequestHttpJobTest, IndividuallyBlockedCookies) {
HttpTestServer test_server;
ASSERT_TRUE(test_server.Start());
auto network_delegate = std::make_unique<FilteringTestNetworkDelegate>();
network_delegate->set_block_get_cookies_by_name(true);
network_delegate->SetCookieFilter("blocked_");
auto context_builder = CreateTestURLRequestContextBuilder();
context_builder->SetCookieStore(std::make_unique<CookieMonster>(
/*store=*/nullptr, /*net_log=*/nullptr));
context_builder->set_network_delegate(std::move(network_delegate));
auto context = context_builder->Build();
// Set cookies.
{
TestDelegate d;
GURL test_url = test_server.GetURL(
"/set-cookie?blocked_one=1;SameSite=Lax;Secure&"
"blocked_two=1;SameSite=Lax;Secure&"
"allowed=1;SameSite=Lax;Secure");
std::unique_ptr<URLRequest> req =
CreateFirstPartyRequest(*context, test_url, &d);
req->Start();
d.RunUntilComplete();
}
// Get cookies.
TestDelegate d;
std::unique_ptr<URLRequest> req = CreateFirstPartyRequest(
*context, test_server.GetURL("/echoheader?Cookie"), &d);
req->Start();
d.RunUntilComplete();
EXPECT_EQ("allowed=1", d.data_received());
EXPECT_THAT(
req->maybe_sent_cookies(),
UnorderedElementsAre(
MatchesCookieWithAccessResult(
MatchesCookieWithName("blocked_one"),
MatchesCookieAccessResult(
HasExactlyExclusionReasonsForTesting(
std::vector<CookieInclusionStatus::ExclusionReason>{
CookieInclusionStatus::EXCLUDE_USER_PREFERENCES}),
_, _, _)),
MatchesCookieWithAccessResult(
MatchesCookieWithName("blocked_two"),
MatchesCookieAccessResult(
HasExactlyExclusionReasonsForTesting(
std::vector<CookieInclusionStatus::ExclusionReason>{
CookieInclusionStatus::EXCLUDE_USER_PREFERENCES}),
_, _, _)),
MatchesCookieWithAccessResult(
MatchesCookieWithName("allowed"),
MatchesCookieAccessResult(IsInclude(), _, _, _))));
}
namespace {
int content_count = 0;
std::unique_ptr<test_server::HttpResponse> IncreaseOnRequest(
const test_server::HttpRequest& request) {
auto http_response = std::make_unique<test_server::BasicHttpResponse>();
http_response->set_content(base::NumberToString(content_count));
content_count++;
return std::move(http_response);
}
void ResetContentCount() {
content_count = 0;
}
} // namespace
TEST_F(URLRequestHttpJobTest, GetFirstPartySetsCacheFilterMatchInfo) {
EmbeddedTestServer https_test(EmbeddedTestServer::TYPE_HTTPS);
https_test.AddDefaultHandlers(base::FilePath());
https_test.RegisterRequestHandler(base::BindRepeating(&IncreaseOnRequest));
ASSERT_TRUE(https_test.Start());
auto context_builder = CreateTestURLRequestContextBuilder();
auto cookie_access_delegate = std::make_unique<TestCookieAccessDelegate>();
TestCookieAccessDelegate* raw_cookie_access_delegate =
cookie_access_delegate.get();
auto cm = std::make_unique<CookieMonster>(nullptr, nullptr);
cm->SetCookieAccessDelegate(std::move(cookie_access_delegate));
context_builder->SetCookieStore(std::move(cm));
auto context = context_builder->Build();
const GURL kTestUrl = https_test.GetURL("/");
const IsolationInfo kTestIsolationInfo =
IsolationInfo::CreateForInternalRequest(url::Origin::Create(kTestUrl));
{
TestDelegate delegate;
std::unique_ptr<URLRequest> req(context->CreateRequest(
kTestUrl, DEFAULT_PRIORITY, &delegate, TRAFFIC_ANNOTATION_FOR_TESTS));
req->set_isolation_info(kTestIsolationInfo);
req->set_allow_credentials(false);
req->Start();
delegate.RunUntilComplete();
EXPECT_EQ("0", delegate.data_received());
}
{ // Test using the cached response.
TestDelegate delegate;
std::unique_ptr<URLRequest> req(context->CreateRequest(
kTestUrl, DEFAULT_PRIORITY, &delegate, TRAFFIC_ANNOTATION_FOR_TESTS));
req->SetLoadFlags(LOAD_SKIP_CACHE_VALIDATION);
req->set_allow_credentials(false);
req->set_isolation_info(kTestIsolationInfo);
req->Start();
delegate.RunUntilComplete();
EXPECT_EQ("0", delegate.data_received());
}
// Set cache filter and test cache is bypassed because the request site has a
// matched entry in the filter and its response cache was stored before being
// marked to clear.
const int64_t kClearAtRunId = 3;
const int64_t kBrowserRunId = 3;
FirstPartySetsCacheFilter cache_filter(
{{SchemefulSite(kTestUrl), kClearAtRunId}}, kBrowserRunId);
raw_cookie_access_delegate->set_first_party_sets_cache_filter(
std::move(cache_filter));
{
TestDelegate delegate;
std::unique_ptr<URLRequest> req(context->CreateRequest(
kTestUrl, DEFAULT_PRIORITY, &delegate, TRAFFIC_ANNOTATION_FOR_TESTS));
req->SetLoadFlags(LOAD_SKIP_CACHE_VALIDATION);
req->set_allow_credentials(false);
req->set_isolation_info(kTestIsolationInfo);
req->Start();
delegate.RunUntilComplete();
EXPECT_EQ("1", delegate.data_received());
}
ResetContentCount();
}
TEST_F(URLRequestHttpJobTest, SetPartitionedCookie) {
EmbeddedTestServer https_test(EmbeddedTestServer::TYPE_HTTPS);
https_test.AddDefaultHandlers(base::FilePath());
ASSERT_TRUE(https_test.Start());
auto context_builder = CreateTestURLRequestContextBuilder();
context_builder->SetCookieStore(std::make_unique<CookieMonster>(
/*store=*/nullptr, /*net_log=*/nullptr));
auto context = context_builder->Build();
const url::Origin kTopFrameOrigin =
url::Origin::Create(GURL("https://www.toplevelsite.com"));
const IsolationInfo kTestIsolationInfo =
IsolationInfo::CreateForInternalRequest(kTopFrameOrigin);
{
TestDelegate delegate;
std::unique_ptr<URLRequest> req(context->CreateRequest(
https_test.GetURL(
"/set-cookie?__Host-foo=bar;SameSite=None;Secure;Path=/"
";Partitioned;"),
DEFAULT_PRIORITY, &delegate, TRAFFIC_ANNOTATION_FOR_TESTS));
req->set_isolation_info(kTestIsolationInfo);
req->Start();
ASSERT_TRUE(req->is_pending());
delegate.RunUntilComplete();
}
{ // Test request from the same top-level site.
TestDelegate delegate;
std::unique_ptr<URLRequest> req(context->CreateRequest(
https_test.GetURL("/echoheader?Cookie"), DEFAULT_PRIORITY, &delegate,
TRAFFIC_ANNOTATION_FOR_TESTS));
req->set_isolation_info(kTestIsolationInfo);
req->Start();
delegate.RunUntilComplete();
EXPECT_EQ("__Host-foo=bar", delegate.data_received());
}
{ // Test request from a different top-level site.
const url::Origin kOtherTopFrameOrigin =
url::Origin::Create(GURL("https://www.anothertoplevelsite.com"));
const IsolationInfo kOtherTestIsolationInfo =
IsolationInfo::CreateForInternalRequest(kOtherTopFrameOrigin);
TestDelegate delegate;
std::unique_ptr<URLRequest> req(context->CreateRequest(
https_test.GetURL("/echoheader?Cookie"), DEFAULT_PRIORITY, &delegate,
TRAFFIC_ANNOTATION_FOR_TESTS));
req->set_isolation_info(kOtherTestIsolationInfo);
req->Start();
delegate.RunUntilComplete();
EXPECT_EQ("None", delegate.data_received());
}
{ // Test request from same top-level eTLD+1 but different scheme. Note that
// although the top-level site is insecure, the endpoint setting/receiving
// the cookie is always secure.
const url::Origin kHttpTopFrameOrigin =
url::Origin::Create(GURL("http://www.toplevelsite.com"));
const IsolationInfo kHttpTestIsolationInfo =
IsolationInfo::CreateForInternalRequest(kHttpTopFrameOrigin);
TestDelegate delegate;
std::unique_ptr<URLRequest> req(context->CreateRequest(
https_test.GetURL("/echoheader?Cookie"), DEFAULT_PRIORITY, &delegate,
TRAFFIC_ANNOTATION_FOR_TESTS));
req->set_isolation_info(kHttpTestIsolationInfo);
req->Start();
delegate.RunUntilComplete();
EXPECT_EQ("None", delegate.data_received());
}
}
TEST_F(URLRequestHttpJobTest, PartitionedCookiePrivacyMode) {
EmbeddedTestServer https_test(EmbeddedTestServer::TYPE_HTTPS);
https_test.AddDefaultHandlers(base::FilePath());
ASSERT_TRUE(https_test.Start());
auto context_builder = CreateTestURLRequestContextBuilder();
context_builder->SetCookieStore(
std::make_unique<CookieMonster>(/*store=*/nullptr, /*net_log=*/nullptr));
auto& network_delegate = *context_builder->set_network_delegate(
std::make_unique<FilteringTestNetworkDelegate>());
auto context = context_builder->Build();
const url::Origin kTopFrameOrigin =
url::Origin::Create(GURL("https://www.toplevelsite.com"));
const IsolationInfo kTestIsolationInfo =
IsolationInfo::CreateForInternalRequest(kTopFrameOrigin);
{
// Set an unpartitioned and partitioned cookie.
TestDelegate delegate;
std::unique_ptr<URLRequest> req(context->CreateRequest(
https_test.GetURL(
"/set-cookie?__Host-partitioned=0;SameSite=None;Secure;Path=/"
";Partitioned;&__Host-unpartitioned=1;SameSite=None;Secure;Path=/"),
DEFAULT_PRIORITY, &delegate, TRAFFIC_ANNOTATION_FOR_TESTS));
req->set_isolation_info(kTestIsolationInfo);
req->Start();
ASSERT_TRUE(req->is_pending());
delegate.RunUntilComplete();
}
{ // Get both cookies when privacy mode is disabled.
TestDelegate delegate;
std::unique_ptr<URLRequest> req(context->CreateRequest(
https_test.GetURL("/echoheader?Cookie"), DEFAULT_PRIORITY, &delegate,
TRAFFIC_ANNOTATION_FOR_TESTS));
req->set_isolation_info(kTestIsolationInfo);
req->Start();
delegate.RunUntilComplete();
EXPECT_EQ("__Host-partitioned=0; __Host-unpartitioned=1",
delegate.data_received());
}
{ // Get cookies with privacy mode enabled and partitioned state allowed.
network_delegate.set_force_privacy_mode(true);
network_delegate.set_partitioned_state_allowed(true);
network_delegate.SetCookieFilter("unpartitioned");
network_delegate.set_block_get_cookies_by_name(true);
TestDelegate delegate;
std::unique_ptr<URLRequest> req(context->CreateRequest(
https_test.GetURL("/echoheader?Cookie"), DEFAULT_PRIORITY, &delegate,
TRAFFIC_ANNOTATION_FOR_TESTS));
req->set_isolation_info(kTestIsolationInfo);
req->Start();
delegate.RunUntilComplete();
EXPECT_EQ("__Host-partitioned=0", delegate.data_received());
auto want_exclusion_reasons =
std::vector<CookieInclusionStatus::ExclusionReason>{};
EXPECT_THAT(
req->maybe_sent_cookies(),
UnorderedElementsAre(
MatchesCookieWithAccessResult(
MatchesCookieWithName("__Host-partitioned"),
MatchesCookieAccessResult(HasExactlyExclusionReasonsForTesting(
want_exclusion_reasons),
_, _, _)),
MatchesCookieWithAccessResult(
MatchesCookieWithName("__Host-unpartitioned"),
MatchesCookieAccessResult(
HasExactlyExclusionReasonsForTesting(
std::vector<CookieInclusionStatus::ExclusionReason>{
CookieInclusionStatus::EXCLUDE_USER_PREFERENCES}),
_, _, _))));
}
{ // Get cookies with privacy mode enabled and partitioned state is not
// allowed.
network_delegate.set_force_privacy_mode(true);
network_delegate.set_partitioned_state_allowed(false);
TestDelegate delegate;
std::unique_ptr<URLRequest> req(context->CreateRequest(
https_test.GetURL("/echoheader?Cookie"), DEFAULT_PRIORITY, &delegate,
TRAFFIC_ANNOTATION_FOR_TESTS));
req->set_isolation_info(kTestIsolationInfo);
req->Start();
delegate.RunUntilComplete();
EXPECT_EQ("None", delegate.data_received());
EXPECT_THAT(
req->maybe_sent_cookies(),
UnorderedElementsAre(
MatchesCookieWithAccessResult(
MatchesCookieWithName("__Host-partitioned"),
MatchesCookieAccessResult(
HasExactlyExclusionReasonsForTesting(
std::vector<CookieInclusionStatus::ExclusionReason>{
CookieInclusionStatus::EXCLUDE_USER_PREFERENCES}),
_, _, _)),
MatchesCookieWithAccessResult(
MatchesCookieWithName("__Host-unpartitioned"),
MatchesCookieAccessResult(
HasExactlyExclusionReasonsForTesting(
std::vector<CookieInclusionStatus::ExclusionReason>{
CookieInclusionStatus::EXCLUDE_USER_PREFERENCES}),
_, _, _))));
}
}
} // namespace net