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