1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
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 "content/browser/resolve_proxy_msg_helper.h"
6
7 #include <tuple>
8
9 #include "base/memory/ptr_util.h"
10 #include "base/run_loop.h"
11 #include "content/common/view_messages.h"
12 #include "content/public/test/browser_task_environment.h"
13 #include "ipc/ipc_test_sink.h"
14 #include "mojo/public/cpp/bindings/remote.h"
15 #include "net/base/net_errors.h"
16 #include "net/proxy_resolution/proxy_info.h"
17 #include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
18 #include "services/network/public/mojom/proxy_lookup_client.mojom.h"
19 #include "testing/gtest/include/gtest/gtest.h"
20
21 namespace content {
22
23 class TestResolveProxyMsgHelper : public ResolveProxyMsgHelper {
24 public:
25 // Incoming mojo::Remote<ProxyLookupClient>s are written to
26 // |proxy_lookup_client|.
TestResolveProxyMsgHelper(IPC::Listener * listener,mojo::Remote<network::mojom::ProxyLookupClient> * proxy_lookup_client)27 explicit TestResolveProxyMsgHelper(
28 IPC::Listener* listener,
29 mojo::Remote<network::mojom::ProxyLookupClient>* proxy_lookup_client)
30 : ResolveProxyMsgHelper(0 /* renderer_process_host_id */),
31 listener_(listener),
32 proxy_lookup_client_(proxy_lookup_client) {}
33
Send(IPC::Message * message)34 bool Send(IPC::Message* message) override {
35 // Shouldn't be calling Send() if there are no live references to |this|.
36 EXPECT_TRUE(HasAtLeastOneRef());
37
38 listener_->OnMessageReceived(*message);
39 delete message;
40 return true;
41 }
42
SendRequestToNetworkService(const GURL & url,mojo::PendingRemote<network::mojom::ProxyLookupClient> proxy_lookup_client)43 bool SendRequestToNetworkService(
44 const GURL& url,
45 mojo::PendingRemote<network::mojom::ProxyLookupClient>
46 proxy_lookup_client) override {
47 // Only one request should be send at a time.
48 EXPECT_FALSE(*proxy_lookup_client_);
49
50 if (fail_to_send_request_)
51 return false;
52
53 pending_url_ = url;
54 proxy_lookup_client_->Bind(std::move(proxy_lookup_client));
55 return true;
56 }
57
pending_url() const58 const GURL& pending_url() const { return pending_url_; }
59
set_fail_to_send_request(bool fail_to_send_request)60 void set_fail_to_send_request(bool fail_to_send_request) {
61 fail_to_send_request_ = fail_to_send_request;
62 }
63
64 protected:
~TestResolveProxyMsgHelper()65 ~TestResolveProxyMsgHelper() override {}
66
67 IPC::Listener* listener_;
68
69 bool fail_to_send_request_ = false;
70
71 mojo::Remote<network::mojom::ProxyLookupClient>* proxy_lookup_client_;
72 GURL pending_url_;
73
74 DISALLOW_COPY_AND_ASSIGN(TestResolveProxyMsgHelper);
75 };
76
77 class ResolveProxyMsgHelperTest : public testing::Test, public IPC::Listener {
78 public:
79 struct PendingResult {
PendingResultcontent::ResolveProxyMsgHelperTest::PendingResult80 PendingResult(bool result,
81 const std::string& proxy_list)
82 : result(result), proxy_list(proxy_list) {
83 }
84
85 bool result;
86 std::string proxy_list;
87 };
88
ResolveProxyMsgHelperTest()89 ResolveProxyMsgHelperTest()
90 : helper_(base::MakeRefCounted<TestResolveProxyMsgHelper>(
91 this,
92 &proxy_lookup_client_)) {
93 test_sink_.AddFilter(this);
94 }
95
96 protected:
pending_result() const97 const PendingResult* pending_result() const { return pending_result_.get(); }
98
clear_pending_result()99 void clear_pending_result() {
100 pending_result_.reset();
101 }
102
GenerateReply()103 IPC::Message* GenerateReply() {
104 bool temp_bool;
105 std::string temp_string;
106 ViewHostMsg_ResolveProxy message(GURL(), &temp_bool, &temp_string);
107 return IPC::SyncMessage::GenerateReply(&message);
108 }
109
OnMessageReceived(const IPC::Message & msg)110 bool OnMessageReceived(const IPC::Message& msg) override {
111 ViewHostMsg_ResolveProxy::ReplyParam reply_data;
112 EXPECT_TRUE(ViewHostMsg_ResolveProxy::ReadReplyParam(&msg, &reply_data));
113 DCHECK(!pending_result_.get());
114 pending_result_.reset(
115 new PendingResult(std::get<0>(reply_data), std::get<1>(reply_data)));
116 test_sink_.ClearMessages();
117 return true;
118 }
119
120 BrowserTaskEnvironment task_environment_;
121
122 scoped_refptr<TestResolveProxyMsgHelper> helper_;
123 std::unique_ptr<PendingResult> pending_result_;
124
125 mojo::Remote<network::mojom::ProxyLookupClient> proxy_lookup_client_;
126
127 IPC::TestSink test_sink_;
128 };
129
130 // Issue three sequential requests -- each should succeed.
TEST_F(ResolveProxyMsgHelperTest,Sequential)131 TEST_F(ResolveProxyMsgHelperTest, Sequential) {
132 GURL url1("http://www.google1.com/");
133 GURL url2("http://www.google2.com/");
134 GURL url3("http://www.google3.com/");
135
136 // Messages are deleted by the sink.
137 IPC::Message* msg1 = GenerateReply();
138 IPC::Message* msg2 = GenerateReply();
139 IPC::Message* msg3 = GenerateReply();
140
141 // Execute each request sequentially (so there are never 2 requests
142 // outstanding at the same time).
143
144 helper_->OnResolveProxy(url1, msg1);
145
146 // There should be a pending proxy lookup request. Respond to it.
147 EXPECT_EQ(url1, helper_->pending_url());
148 ASSERT_TRUE(proxy_lookup_client_);
149 net::ProxyInfo proxy_info;
150 proxy_info.UseNamedProxy("result1:80");
151 proxy_lookup_client_->OnProxyLookupComplete(net::OK, proxy_info);
152 proxy_lookup_client_.reset();
153 base::RunLoop().RunUntilIdle();
154
155 // Check result.
156 EXPECT_EQ(true, pending_result()->result);
157 EXPECT_EQ("PROXY result1:80", pending_result()->proxy_list);
158 clear_pending_result();
159
160 helper_->OnResolveProxy(url2, msg2);
161
162 EXPECT_EQ(url2, helper_->pending_url());
163 ASSERT_TRUE(proxy_lookup_client_);
164 proxy_info.UseNamedProxy("result2:80");
165 proxy_lookup_client_->OnProxyLookupComplete(net::OK, proxy_info);
166 proxy_lookup_client_.reset();
167 base::RunLoop().RunUntilIdle();
168
169 // Check result.
170 EXPECT_EQ(true, pending_result()->result);
171 EXPECT_EQ("PROXY result2:80", pending_result()->proxy_list);
172 clear_pending_result();
173
174 helper_->OnResolveProxy(url3, msg3);
175
176 EXPECT_EQ(url3, helper_->pending_url());
177 ASSERT_TRUE(proxy_lookup_client_);
178 proxy_info.UseNamedProxy("result3:80");
179 proxy_lookup_client_->OnProxyLookupComplete(net::OK, proxy_info);
180 base::RunLoop().RunUntilIdle();
181
182 // Check result.
183 EXPECT_EQ(true, pending_result()->result);
184 EXPECT_EQ("PROXY result3:80", pending_result()->proxy_list);
185 clear_pending_result();
186 }
187
188 // Issue a request while one is already in progress -- should be queued.
TEST_F(ResolveProxyMsgHelperTest,QueueRequests)189 TEST_F(ResolveProxyMsgHelperTest, QueueRequests) {
190 GURL url1("http://www.google1.com/");
191 GURL url2("http://www.google2.com/");
192 GURL url3("http://www.google3.com/");
193
194 IPC::Message* msg1 = GenerateReply();
195 IPC::Message* msg2 = GenerateReply();
196 IPC::Message* msg3 = GenerateReply();
197
198 // Start three requests. All the requests will be pending.
199
200 helper_->OnResolveProxy(url1, msg1);
201 helper_->OnResolveProxy(url2, msg2);
202 helper_->OnResolveProxy(url3, msg3);
203
204 // Complete first request.
205 EXPECT_EQ(url1, helper_->pending_url());
206 ASSERT_TRUE(proxy_lookup_client_);
207 net::ProxyInfo proxy_info;
208 proxy_info.UseNamedProxy("result1:80");
209 proxy_lookup_client_->OnProxyLookupComplete(net::OK, proxy_info);
210 proxy_lookup_client_.reset();
211 base::RunLoop().RunUntilIdle();
212
213 // Check result.
214 EXPECT_EQ(true, pending_result()->result);
215 EXPECT_EQ("PROXY result1:80", pending_result()->proxy_list);
216 clear_pending_result();
217
218 // Complete second request.
219 EXPECT_EQ(url2, helper_->pending_url());
220 ASSERT_TRUE(proxy_lookup_client_);
221 proxy_info.UseNamedProxy("result2:80");
222 proxy_lookup_client_->OnProxyLookupComplete(net::OK, proxy_info);
223 proxy_lookup_client_.reset();
224 base::RunLoop().RunUntilIdle();
225
226 // Check result.
227 EXPECT_EQ(true, pending_result()->result);
228 EXPECT_EQ("PROXY result2:80", pending_result()->proxy_list);
229 clear_pending_result();
230
231 // Complete third request.
232 EXPECT_EQ(url3, helper_->pending_url());
233 ASSERT_TRUE(proxy_lookup_client_);
234 proxy_info.UseNamedProxy("result3:80");
235 proxy_lookup_client_->OnProxyLookupComplete(net::OK, proxy_info);
236 base::RunLoop().RunUntilIdle();
237
238 // Check result.
239 EXPECT_EQ(true, pending_result()->result);
240 EXPECT_EQ("PROXY result3:80", pending_result()->proxy_list);
241 clear_pending_result();
242 }
243
244 // Delete the helper while a request is in progress and others are pending.
TEST_F(ResolveProxyMsgHelperTest,CancelPendingRequests)245 TEST_F(ResolveProxyMsgHelperTest, CancelPendingRequests) {
246 GURL url1("http://www.google1.com/");
247 GURL url2("http://www.google2.com/");
248 GURL url3("http://www.google3.com/");
249
250 // They will be deleted by the request's cancellation.
251 IPC::Message* msg1 = GenerateReply();
252 IPC::Message* msg2 = GenerateReply();
253 IPC::Message* msg3 = GenerateReply();
254
255 // Start three requests. Since the proxy resolver is async, all the
256 // requests will be pending.
257
258 helper_->OnResolveProxy(url1, msg1);
259 helper_->OnResolveProxy(url2, msg2);
260 helper_->OnResolveProxy(url3, msg3);
261
262 // Check the first request is pending.
263 EXPECT_EQ(url1, helper_->pending_url());
264 ASSERT_TRUE(proxy_lookup_client_);
265
266 // Release a reference. The |helper_| will not be deleted, since there's a
267 // pending resolution.
268 helper_ = nullptr;
269 base::RunLoop().RunUntilIdle();
270 EXPECT_TRUE(proxy_lookup_client_.is_bound());
271 EXPECT_FALSE(!proxy_lookup_client_.is_connected());
272
273 // Send Mojo message on the pipe.
274 net::ProxyInfo proxy_info;
275 proxy_info.UseNamedProxy("result1:80");
276 proxy_lookup_client_->OnProxyLookupComplete(net::OK, proxy_info);
277
278 // Spinning the message loop results in the helper being destroyed and closing
279 // the pipe.
280 base::RunLoop().RunUntilIdle();
281 EXPECT_TRUE(!proxy_lookup_client_.is_bound() ||
282 !proxy_lookup_client_.is_connected());
283 // The result should not have been sent.
284 EXPECT_FALSE(pending_result());
285
286 // It should also be the case that msg1, msg2, msg3 were deleted by the
287 // cancellation. (Else will show up as a leak).
288 }
289
290 // Issue a request that fails.
TEST_F(ResolveProxyMsgHelperTest,RequestFails)291 TEST_F(ResolveProxyMsgHelperTest, RequestFails) {
292 GURL url("http://www.google.com/");
293
294 // Message will be deleted by the sink.
295 IPC::Message* msg = GenerateReply();
296
297 helper_->OnResolveProxy(url, msg);
298
299 // There should be a pending proxy lookup request. Respond to it.
300 EXPECT_EQ(url, helper_->pending_url());
301 ASSERT_TRUE(proxy_lookup_client_);
302 proxy_lookup_client_->OnProxyLookupComplete(net::ERR_FAILED, base::nullopt);
303 base::RunLoop().RunUntilIdle();
304
305 // Check result.
306 EXPECT_EQ(false, pending_result()->result);
307 EXPECT_EQ("", pending_result()->proxy_list);
308 clear_pending_result();
309 }
310
311 // Issue a request, only to have the Mojo pipe closed.
TEST_F(ResolveProxyMsgHelperTest,PipeClosed)312 TEST_F(ResolveProxyMsgHelperTest, PipeClosed) {
313 GURL url("http://www.google.com/");
314
315 // Message will be deleted by the sink.
316 IPC::Message* msg = GenerateReply();
317
318 helper_->OnResolveProxy(url, msg);
319
320 // There should be a pending proxy lookup request. Respond to it by closing
321 // the pipe.
322 EXPECT_EQ(url, helper_->pending_url());
323 ASSERT_TRUE(proxy_lookup_client_);
324 proxy_lookup_client_.reset();
325 base::RunLoop().RunUntilIdle();
326
327 // Check result.
328 EXPECT_EQ(false, pending_result()->result);
329 EXPECT_EQ("", pending_result()->proxy_list);
330 clear_pending_result();
331 }
332
333 // Fail to send a request to the network service.
TEST_F(ResolveProxyMsgHelperTest,FailToSendRequest)334 TEST_F(ResolveProxyMsgHelperTest, FailToSendRequest) {
335 GURL url("http://www.google.com/");
336
337 // Message will be deleted by the sink.
338 IPC::Message* msg = GenerateReply();
339
340 helper_->set_fail_to_send_request(true);
341
342 helper_->OnResolveProxy(url, msg);
343 // No request should be pending.
344 EXPECT_TRUE(helper_->pending_url().is_empty());
345
346 // Check result.
347 EXPECT_EQ(false, pending_result()->result);
348 EXPECT_EQ("", pending_result()->proxy_list);
349 clear_pending_result();
350 }
351
352 // Make sure if mojo callback is invoked after last externally owned reference
353 // is released, there is no crash.
354 // Regression test for https://crbug.com/870675
TEST_F(ResolveProxyMsgHelperTest,Lifetime)355 TEST_F(ResolveProxyMsgHelperTest, Lifetime) {
356 GURL url("http://www.google1.com/");
357
358 // Messages are deleted by the sink.
359 IPC::Message* msg = GenerateReply();
360
361 helper_->OnResolveProxy(url, msg);
362
363 // There should be a pending proxy lookup request. Respond to it.
364 EXPECT_EQ(url, helper_->pending_url());
365 ASSERT_TRUE(proxy_lookup_client_);
366
367 // Release the |helper_| pointer. The object should keep a reference to
368 // itself, so should not be deleted.
369 helper_ = nullptr;
370 base::RunLoop().RunUntilIdle();
371 EXPECT_TRUE(proxy_lookup_client_.is_bound());
372 EXPECT_FALSE(!proxy_lookup_client_.is_connected());
373
374 // Send Mojo message on the pipe.
375 net::ProxyInfo proxy_info;
376 proxy_info.UseNamedProxy("result1:80");
377 proxy_lookup_client_->OnProxyLookupComplete(net::OK, proxy_info);
378
379 // Spinning the message loop results in the helper being destroyed and closing
380 // the pipe.
381 base::RunLoop().RunUntilIdle();
382 EXPECT_TRUE(!proxy_lookup_client_.is_bound() ||
383 !proxy_lookup_client_.is_connected());
384 // The result should not have been sent.
385 EXPECT_FALSE(pending_result());
386 }
387
388 } // namespace content
389