1 // Copyright 2022 The Chromium Authors
2 // 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
9 #include "base/task/single_thread_task_runner.h"
10 #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"
18 #include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
19 #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
27 namespace cronet {
28
29 namespace {
30
31 class NoOpCronetContextCallback : public CronetContext::Callback {
32 public:
33 NoOpCronetContextCallback() = default;
34
35 NoOpCronetContextCallback(const NoOpCronetContextCallback&) = delete;
36 NoOpCronetContextCallback& operator=(const NoOpCronetContextCallback&) =
37 delete;
38
OnInitNetworkThread()39 void OnInitNetworkThread() override {}
40
OnDestroyNetworkThread()41 void OnDestroyNetworkThread() override {}
42
OnEffectiveConnectionTypeChanged(net::EffectiveConnectionType effective_connection_type)43 void OnEffectiveConnectionTypeChanged(
44 net::EffectiveConnectionType effective_connection_type) override {}
45
OnRTTOrThroughputEstimatesComputed(int32_t http_rtt_ms,int32_t transport_rtt_ms,int32_t downstream_throughput_kbps)46 void OnRTTOrThroughputEstimatesComputed(
47 int32_t http_rtt_ms,
48 int32_t transport_rtt_ms,
49 int32_t downstream_throughput_kbps) override {}
50
OnRTTObservation(int32_t rtt_ms,int32_t timestamp_ms,net::NetworkQualityObservationSource source)51 void OnRTTObservation(int32_t rtt_ms,
52 int32_t timestamp_ms,
53 net::NetworkQualityObservationSource source) override {}
54
OnThroughputObservation(int32_t throughput_kbps,int32_t timestamp_ms,net::NetworkQualityObservationSource source)55 void OnThroughputObservation(
56 int32_t throughput_kbps,
57 int32_t timestamp_ms,
58 net::NetworkQualityObservationSource source) override {}
59
OnStopNetLogCompleted()60 void OnStopNetLogCompleted() override {}
61
62 ~NoOpCronetContextCallback() override = default;
63 };
64
CreateSimpleURLRequestContextConfig()65 std::unique_ptr<URLRequestContextConfig> CreateSimpleURLRequestContextConfig() {
66 return URLRequestContextConfig::CreateURLRequestContextConfig(
67 // Enable QUIC.
68 true,
69 // 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.
95 std::nullopt);
96 }
97
98 class NetworkTasksTest : public testing::Test {
99 protected:
NetworkTasksTest()100 NetworkTasksTest()
101 : 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")),
106 network_tasks_(new CronetContext::NetworkTasks(
107 CreateSimpleURLRequestContextConfig(),
108 std::make_unique<NoOpCronetContextCallback>())) {
109 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();
118 Initialize();
119 }
120
~NetworkTasksTest()121 ~NetworkTasksTest() override {
122 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_)));
126 }
127
Initialize()128 void Initialize() {
129 PostToNetworkThreadSync(
130 base::BindOnce(&CronetContext::NetworkTasks::Initialize,
131 base::Unretained(network_tasks_.get()),
132 network_task_runner_, file_task_runner_,
133 std::make_unique<net::ProxyConfigServiceFixed>(
134 net::ProxyConfigWithAnnotation::CreateDirect())));
135 }
136
SpawnNetworkBoundURLRequestContext(net::handles::NetworkHandle network)137 void SpawnNetworkBoundURLRequestContext(net::handles::NetworkHandle network) {
138 PostToNetworkThreadSync(base::BindLambdaForTesting([=]() {
139 network_tasks_->SpawnNetworkBoundURLRequestContextForTesting(network);
140 }));
141 }
142
CheckURLRequestContextExistence(net::handles::NetworkHandle network,bool expected)143 void CheckURLRequestContextExistence(net::handles::NetworkHandle network,
144 bool expected) {
145 std::atomic_bool context_exists = false;
146 PostToNetworkThreadSync(base::BindLambdaForTesting([&]() {
147 context_exists.store(
148 network_tasks_->URLRequestContextExistsForTesting(network));
149 }));
150 EXPECT_EQ(expected, context_exists.load());
151 }
152
CreateURLRequest(net::handles::NetworkHandle network)153 void CreateURLRequest(net::handles::NetworkHandle network) {
154 std::atomic_bool url_request_created = false;
155 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);
160 url_request_created = !!url_request_;
161 }));
162 EXPECT_TRUE(url_request_created);
163 }
164
ReleaseURLRequest()165 void ReleaseURLRequest() {
166 PostToNetworkThreadSync(
167 base::BindLambdaForTesting([&]() { url_request_.reset(); }));
168 }
169
MaybeDestroyURLRequestContext(net::handles::NetworkHandle network)170 void MaybeDestroyURLRequestContext(net::handles::NetworkHandle network) {
171 PostToNetworkThreadSync(base::BindLambdaForTesting(
172 [&]() { network_tasks_->MaybeDestroyURLRequestContext(network); }));
173 }
174
PostToNetworkThreadSync(base::OnceCallback<void ()> callback)175 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_;
186 std::unique_ptr<net::test::ScopedMockNetworkChangeNotifier> scoped_ncn_;
187 std::unique_ptr<base::Thread> network_thread_;
188 std::unique_ptr<base::Thread> file_thread_;
189 scoped_refptr<base::SingleThreadTaskRunner> network_task_runner_;
190 scoped_refptr<base::SingleThreadTaskRunner> file_task_runner_;
191 std::unique_ptr<CronetContext::NetworkTasks> network_tasks_;
192 std::unique_ptr<net::URLRequest> url_request_;
193 };
194
TEST_F(NetworkTasksTest,NetworkBoundContextLifetime)195 TEST_F(NetworkTasksTest, NetworkBoundContextLifetime) {
196 #if BUILDFLAG(IS_ANDROID)
197 if (base::android::BuildInfo::GetInstance()->sdk_int() <
198 base::android::SDK_VERSION_MARSHMALLOW) {
199 GTEST_SKIP() << "Network binding on Android requires an API level >= 23";
200 }
201
202 constexpr net::handles::NetworkHandle kNetwork = 1;
203
204 CheckURLRequestContextExistence(kNetwork, false);
205 SpawnNetworkBoundURLRequestContext(kNetwork);
206 CheckURLRequestContextExistence(kNetwork, true);
207
208 // Once the network disconnects the context should be destroyed.
209 scoped_ncn_->mock_network_change_notifier()->NotifyNetworkDisconnected(
210 kNetwork);
211 CheckURLRequestContextExistence(kNetwork, false);
212 #else
213 GTEST_SKIP() << "Network binding is supported only on Android";
214 #endif // BUILDFLAG(IS_ANDROID)
215 }
216
TEST_F(NetworkTasksTest,NetworkBoundContextWithPendingRequest)217 TEST_F(NetworkTasksTest, NetworkBoundContextWithPendingRequest) {
218 #if BUILDFLAG(IS_ANDROID)
219 if (base::android::BuildInfo::GetInstance()->sdk_int() <
220 base::android::SDK_VERSION_MARSHMALLOW) {
221 GTEST_SKIP() << "Network binding on Android requires an API level >= 23";
222 }
223
224 constexpr net::handles::NetworkHandle kNetwork = 1;
225
226 CheckURLRequestContextExistence(kNetwork, false);
227 SpawnNetworkBoundURLRequestContext(kNetwork);
228 CheckURLRequestContextExistence(kNetwork, true);
229
230 // If after a network disconnection there are still pending requests, the
231 // context should not be destroyed to avoid UAFs (URLRequests can reference
232 // their associated URLRequestContext).
233 CreateURLRequest(kNetwork);
234 CheckURLRequestContextExistence(kNetwork, true);
235 scoped_ncn_->mock_network_change_notifier()->QueueNetworkDisconnected(
236 kNetwork);
237 CheckURLRequestContextExistence(kNetwork, true);
238
239 // Once the URLRequest is destroyed, MaybeDestroyURLRequestContext should be
240 // able to destroy the context.
241 ReleaseURLRequest();
242 MaybeDestroyURLRequestContext(kNetwork);
243 CheckURLRequestContextExistence(kNetwork, false);
244 #else
245 GTEST_SKIP() << "Network binding is supported only on Android";
246 #endif // BUILDFLAG(IS_ANDROID)
247 }
248
249 } // namespace
250
251 } // namespace cronet
252