blob: 98faf647a93d38256281135f8ec8f21b2c6da037 [file] [log] [blame]
Avi Drissman8ba1bad2022-09-13 19:22:361// Copyright 2022 The Chromium Authors
Stefano Duoda85dc02022-03-10 14:07:292// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "components/cronet/cronet_context.h"
6
7#include <latch>
8
Sean Mahere672a662023-01-09 21:42:289#include "base/task/single_thread_task_runner.h"
Stefano Duoda85dc02022-03-10 14:07:2910#include "base/test/bind.h"
11#include "base/test/task_environment.h"
12#include "components/cronet/cronet_global_state.h"
13#include "components/cronet/url_request_context_config.h"
14#include "net/base/mock_network_change_notifier.h"
15#include "net/base/request_priority.h"
16#include "net/cert/cert_verifier.h"
17#include "net/proxy_resolution/proxy_config_service_fixed.h"
Stefano Duo0d3e86d2022-03-24 13:09:1018#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
Stefano Duoda85dc02022-03-10 14:07:2919#include "net/url_request/url_request.h"
20#include "net/url_request/url_request_context.h"
21#include "testing/gtest/include/gtest/gtest.h"
22
23#if BUILDFLAG(IS_ANDROID)
24#include "base/android/build_info.h"
25#endif // BUILDFLAG(IS_ANDROID)
26
27namespace cronet {
28
29namespace {
30
31class NoOpCronetContextCallback : public CronetContext::Callback {
32 public:
33 NoOpCronetContextCallback() = default;
34
35 NoOpCronetContextCallback(const NoOpCronetContextCallback&) = delete;
36 NoOpCronetContextCallback& operator=(const NoOpCronetContextCallback&) =
37 delete;
38
39 void OnInitNetworkThread() override {}
40
41 void OnDestroyNetworkThread() override {}
42
43 void OnEffectiveConnectionTypeChanged(
44 net::EffectiveConnectionType effective_connection_type) override {}
45
46 void OnRTTOrThroughputEstimatesComputed(
47 int32_t http_rtt_ms,
48 int32_t transport_rtt_ms,
49 int32_t downstream_throughput_kbps) override {}
50
51 void OnRTTObservation(int32_t rtt_ms,
52 int32_t timestamp_ms,
53 net::NetworkQualityObservationSource source) override {}
54
55 void OnThroughputObservation(
56 int32_t throughput_kbps,
57 int32_t timestamp_ms,
58 net::NetworkQualityObservationSource source) override {}
59
60 void OnStopNetLogCompleted() override {}
61
62 ~NoOpCronetContextCallback() override = default;
63};
64
65std::unique_ptr<URLRequestContextConfig> CreateSimpleURLRequestContextConfig() {
66 return URLRequestContextConfig::CreateURLRequestContextConfig(
67 // Enable QUIC.
68 true,
Stefano Duoda85dc02022-03-10 14:07:2969 // Enable SPDY.
70 true,
71 // Enable Brotli.
72 false,
73 // Type of http cache.
74 URLRequestContextConfig::HttpCacheType::DISK,
75 // Max size of http cache in bytes.
76 1024000,
77 // Disable caching for HTTP responses. Other information may be stored
78 // in the cache.
79 false,
80 // Storage path for http cache and cookie storage.
81 "/data/data/org.chromium.net/app_cronet_test/test_storage",
82 // Accept-Language request header field.
83 "foreign-language",
84 // User-Agent request header field.
85 "fake agent",
86 // JSON encoded experimental options.
87 "",
88 // MockCertVerifier to use for testing purposes.
89 std::unique_ptr<net::CertVerifier>(),
90 // Enable network quality estimator.
91 false,
92 // Enable Public Key Pinning bypass for local trust anchors.
93 true,
94 // Optional network thread priority.
Arthur Sonzognic571efb2024-01-26 20:26:1895 std::nullopt);
Stefano Duoda85dc02022-03-10 14:07:2996}
97
98class NetworkTasksTest : public testing::Test {
99 protected:
100 NetworkTasksTest()
Stefano Duo6b82fbc2022-08-17 08:20:30101 : ncn_(net::NetworkChangeNotifier::CreateMockIfNeeded()),
102 scoped_ncn_(
103 std::make_unique<net::test::ScopedMockNetworkChangeNotifier>()),
104 network_thread_(std::make_unique<base::Thread>("network")),
105 file_thread_(std::make_unique<base::Thread>("Network File Thread")),
Stefano Duoda85dc02022-03-10 14:07:29106 network_tasks_(new CronetContext::NetworkTasks(
107 CreateSimpleURLRequestContextConfig(),
Stefano Duo03391ac2022-03-21 18:20:27108 std::make_unique<NoOpCronetContextCallback>())) {
Stefano Duo6b82fbc2022-08-17 08:20:30109 base::Thread::Options options;
110 options.message_pump_type = base::MessagePumpType::IO;
111 network_thread_->StartWithOptions(std::move(options));
112 network_task_runner_ = network_thread_->task_runner();
113
114 file_thread_->Start();
115 file_task_runner_ = file_thread_->task_runner();
116
117 scoped_ncn_->mock_network_change_notifier()->ForceNetworkHandlesSupported();
Stefano Duoda85dc02022-03-10 14:07:29118 Initialize();
119 }
120
Stefano Duo6b82fbc2022-08-17 08:20:30121 ~NetworkTasksTest() override {
Tom Sepez22c8ba032023-02-23 23:12:37122 PostToNetworkThreadSync(base::BindOnce(
123 // Deletion ocurrs as a result of the argument going out of scope.
124 [](std::unique_ptr<CronetContext::NetworkTasks> tasks_to_be_deleted) {},
125 std::move(network_tasks_)));
Stefano Duo6b82fbc2022-08-17 08:20:30126 }
127
Stefano Duoda85dc02022-03-10 14:07:29128 void Initialize() {
129 PostToNetworkThreadSync(
130 base::BindOnce(&CronetContext::NetworkTasks::Initialize,
Tom Sepez22c8ba032023-02-23 23:12:37131 base::Unretained(network_tasks_.get()),
132 network_task_runner_, file_task_runner_,
Stefano Duoda85dc02022-03-10 14:07:29133 std::make_unique<net::ProxyConfigServiceFixed>(
134 net::ProxyConfigWithAnnotation::CreateDirect())));
135 }
136
Stefano Duo6527ed42022-07-29 09:25:44137 void SpawnNetworkBoundURLRequestContext(net::handles::NetworkHandle network) {
Devon Loehrdcb8b462024-08-01 20:17:28138 PostToNetworkThreadSync(base::BindLambdaForTesting([=, this]() {
Stefano Duoda85dc02022-03-10 14:07:29139 network_tasks_->SpawnNetworkBoundURLRequestContextForTesting(network);
140 }));
141 }
142
Stefano Duo6527ed42022-07-29 09:25:44143 void CheckURLRequestContextExistence(net::handles::NetworkHandle network,
144 bool expected) {
Stefano Duo6b82fbc2022-08-17 08:20:30145 std::atomic_bool context_exists = false;
146 PostToNetworkThreadSync(base::BindLambdaForTesting([&]() {
147 context_exists.store(
148 network_tasks_->URLRequestContextExistsForTesting(network));
Stefano Duoda85dc02022-03-10 14:07:29149 }));
Stefano Duo6b82fbc2022-08-17 08:20:30150 EXPECT_EQ(expected, context_exists.load());
Stefano Duoda85dc02022-03-10 14:07:29151 }
152
Stefano Duo6527ed42022-07-29 09:25:44153 void CreateURLRequest(net::handles::NetworkHandle network) {
Stefano Duo6b82fbc2022-08-17 08:20:30154 std::atomic_bool url_request_created = false;
Stefano Duo0d3e86d2022-03-24 13:09:10155 PostToNetworkThreadSync(base::BindLambdaForTesting([&]() {
156 auto* context = network_tasks_->GetURLRequestContext(network);
157 url_request_ = context->CreateRequest(GURL("http://www.foo.com"),
158 net::DEFAULT_PRIORITY, nullptr,
159 TRAFFIC_ANNOTATION_FOR_TESTS);
Stefano Duo6b82fbc2022-08-17 08:20:30160 url_request_created = !!url_request_;
Stefano Duo0d3e86d2022-03-24 13:09:10161 }));
Stefano Duo6b82fbc2022-08-17 08:20:30162 EXPECT_TRUE(url_request_created);
Stefano Duo0d3e86d2022-03-24 13:09:10163 }
164
165 void ReleaseURLRequest() {
166 PostToNetworkThreadSync(
167 base::BindLambdaForTesting([&]() { url_request_.reset(); }));
168 }
169
Stefano Duo6527ed42022-07-29 09:25:44170 void MaybeDestroyURLRequestContext(net::handles::NetworkHandle network) {
Stefano Duo0d3e86d2022-03-24 13:09:10171 PostToNetworkThreadSync(base::BindLambdaForTesting(
172 [&]() { network_tasks_->MaybeDestroyURLRequestContext(network); }));
173 }
174
Stefano Duoda85dc02022-03-10 14:07:29175 void PostToNetworkThreadSync(base::OnceCallback<void()> callback) {
176 std::latch callback_executed{1};
177 auto wait_for_callback = base::BindLambdaForTesting(
178 [&callback_executed]() { callback_executed.count_down(); });
179 network_task_runner_->PostTask(
180 FROM_HERE, std::move(callback).Then(std::move(wait_for_callback)));
181 callback_executed.wait();
182 }
183
184 base::test::TaskEnvironment task_environment_;
185 std::unique_ptr<net::NetworkChangeNotifier> ncn_;
Stefano Duo6b82fbc2022-08-17 08:20:30186 std::unique_ptr<net::test::ScopedMockNetworkChangeNotifier> scoped_ncn_;
187 std::unique_ptr<base::Thread> network_thread_;
188 std::unique_ptr<base::Thread> file_thread_;
Stefano Duoda85dc02022-03-10 14:07:29189 scoped_refptr<base::SingleThreadTaskRunner> network_task_runner_;
190 scoped_refptr<base::SingleThreadTaskRunner> file_task_runner_;
Tom Sepez22c8ba032023-02-23 23:12:37191 std::unique_ptr<CronetContext::NetworkTasks> network_tasks_;
Stefano Duo0d3e86d2022-03-24 13:09:10192 std::unique_ptr<net::URLRequest> url_request_;
Stefano Duoda85dc02022-03-10 14:07:29193};
194
195TEST_F(NetworkTasksTest, NetworkBoundContextLifetime) {
196#if BUILDFLAG(IS_ANDROID)
Stefano Duo6527ed42022-07-29 09:25:44197 constexpr net::handles::NetworkHandle kNetwork = 1;
Stefano Duoda85dc02022-03-10 14:07:29198
199 CheckURLRequestContextExistence(kNetwork, false);
200 SpawnNetworkBoundURLRequestContext(kNetwork);
201 CheckURLRequestContextExistence(kNetwork, true);
202
203 // Once the network disconnects the context should be destroyed.
Stefano Duo6b82fbc2022-08-17 08:20:30204 scoped_ncn_->mock_network_change_notifier()->NotifyNetworkDisconnected(
Stefano Duoda85dc02022-03-10 14:07:29205 kNetwork);
206 CheckURLRequestContextExistence(kNetwork, false);
Stefano Duo6b82fbc2022-08-17 08:20:30207#else
208 GTEST_SKIP() << "Network binding is supported only on Android";
209#endif // BUILDFLAG(IS_ANDROID)
Stefano Duoda85dc02022-03-10 14:07:29210}
211
Stefano Duo0d3e86d2022-03-24 13:09:10212TEST_F(NetworkTasksTest, NetworkBoundContextWithPendingRequest) {
213#if BUILDFLAG(IS_ANDROID)
Stefano Duo6527ed42022-07-29 09:25:44214 constexpr net::handles::NetworkHandle kNetwork = 1;
Stefano Duo0d3e86d2022-03-24 13:09:10215
216 CheckURLRequestContextExistence(kNetwork, false);
217 SpawnNetworkBoundURLRequestContext(kNetwork);
218 CheckURLRequestContextExistence(kNetwork, true);
219
220 // If after a network disconnection there are still pending requests, the
221 // context should not be destroyed to avoid UAFs (URLRequests can reference
222 // their associated URLRequestContext).
223 CreateURLRequest(kNetwork);
Stefano Duo6b82fbc2022-08-17 08:20:30224 CheckURLRequestContextExistence(kNetwork, true);
225 scoped_ncn_->mock_network_change_notifier()->QueueNetworkDisconnected(
Stefano Duo0d3e86d2022-03-24 13:09:10226 kNetwork);
227 CheckURLRequestContextExistence(kNetwork, true);
228
229 // Once the URLRequest is destroyed, MaybeDestroyURLRequestContext should be
230 // able to destroy the context.
231 ReleaseURLRequest();
232 MaybeDestroyURLRequestContext(kNetwork);
233 CheckURLRequestContextExistence(kNetwork, false);
Stefano Duo6b82fbc2022-08-17 08:20:30234#else
235 GTEST_SKIP() << "Network binding is supported only on Android";
236#endif // BUILDFLAG(IS_ANDROID)
Stefano Duo0d3e86d2022-03-24 13:09:10237}
238
Stefano Duoda85dc02022-03-10 14:07:29239} // namespace
240
241} // namespace cronet