xref: /aosp_15_r20/external/cronet/components/cronet/network_tasks_unittest.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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