| // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "content/browser/resolve_proxy_helper.h" |
| |
| #include <tuple> |
| |
| #include "base/memory/ptr_util.h" |
| #include "base/run_loop.h" |
| #include "base/test/bind_test_util.h" |
| #include "content/common/view_messages.h" |
| #include "content/public/test/browser_task_environment.h" |
| #include "ipc/ipc_test_sink.h" |
| #include "mojo/public/cpp/bindings/remote.h" |
| #include "net/base/net_errors.h" |
| #include "net/proxy_resolution/proxy_info.h" |
| #include "net/traffic_annotation/network_traffic_annotation_test_helper.h" |
| #include "services/network/public/mojom/proxy_lookup_client.mojom.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| namespace content { |
| |
| class TestResolveProxyHelper : public ResolveProxyHelper { |
| public: |
| // Incoming mojo::Remote<ProxyLookupClient>s are written to |
| // |proxy_lookup_client|. |
| explicit TestResolveProxyHelper( |
| mojo::Remote<network::mojom::ProxyLookupClient>* proxy_lookup_client) |
| : ResolveProxyHelper(0 /* renderer_process_host_id */), |
| proxy_lookup_client_(proxy_lookup_client) {} |
| |
| bool SendRequestToNetworkService( |
| const GURL& url, |
| mojo::PendingRemote<network::mojom::ProxyLookupClient> |
| proxy_lookup_client) override { |
| // Only one request should be send at a time. |
| EXPECT_FALSE(*proxy_lookup_client_); |
| |
| if (fail_to_send_request_) |
| return false; |
| |
| pending_url_ = url; |
| proxy_lookup_client_->Bind(std::move(proxy_lookup_client)); |
| return true; |
| } |
| |
| const GURL& pending_url() const { return pending_url_; } |
| |
| void set_fail_to_send_request(bool fail_to_send_request) { |
| fail_to_send_request_ = fail_to_send_request; |
| } |
| |
| protected: |
| ~TestResolveProxyHelper() override = default; |
| |
| bool fail_to_send_request_ = false; |
| |
| mojo::Remote<network::mojom::ProxyLookupClient>* proxy_lookup_client_; |
| GURL pending_url_; |
| |
| DISALLOW_COPY_AND_ASSIGN(TestResolveProxyHelper); |
| }; |
| |
| class ResolveProxyHelperTest : public testing::Test { |
| public: |
| struct PendingResult { |
| PendingResult(bool result, const std::string& proxy_list) |
| : result(result), proxy_list(proxy_list) {} |
| |
| bool result; |
| std::string proxy_list; |
| }; |
| |
| ResolveProxyHelperTest() |
| : helper_(base::MakeRefCounted<TestResolveProxyHelper>( |
| &proxy_lookup_client_)) {} |
| |
| protected: |
| BrowserTaskEnvironment task_environment_; |
| scoped_refptr<TestResolveProxyHelper> helper_; |
| mojo::Remote<network::mojom::ProxyLookupClient> proxy_lookup_client_; |
| }; |
| |
| // Issue three sequential requests -- each should succeed. |
| TEST_F(ResolveProxyHelperTest, Sequential) { |
| GURL url1("http://www.google1.com/"); |
| GURL url2("http://www.google2.com/"); |
| GURL url3("http://www.google3.com/"); |
| |
| // Execute each request sequentially (so there are never 2 requests |
| // outstanding at the same time). |
| base::Optional<std::string> result_proxy_list; |
| helper_->ResolveProxy(url1, |
| base::BindLambdaForTesting( |
| [&](const base::Optional<std::string>& proxy_list) { |
| result_proxy_list = proxy_list; |
| })); |
| |
| // There should be a pending proxy lookup request. Respond to it. |
| EXPECT_EQ(url1, helper_->pending_url()); |
| ASSERT_TRUE(proxy_lookup_client_); |
| net::ProxyInfo proxy_info; |
| proxy_info.UseNamedProxy("result1:80"); |
| proxy_lookup_client_->OnProxyLookupComplete(net::OK, proxy_info); |
| proxy_lookup_client_.reset(); |
| base::RunLoop().RunUntilIdle(); |
| |
| // Check result. |
| EXPECT_TRUE(result_proxy_list.has_value()); |
| EXPECT_EQ("PROXY result1:80", result_proxy_list.value()); |
| result_proxy_list.reset(); |
| |
| helper_->ResolveProxy(url2, |
| base::BindLambdaForTesting( |
| [&](const base::Optional<std::string>& proxy_list) { |
| result_proxy_list = proxy_list; |
| })); |
| |
| EXPECT_EQ(url2, helper_->pending_url()); |
| ASSERT_TRUE(proxy_lookup_client_); |
| proxy_info.UseNamedProxy("result2:80"); |
| proxy_lookup_client_->OnProxyLookupComplete(net::OK, proxy_info); |
| proxy_lookup_client_.reset(); |
| base::RunLoop().RunUntilIdle(); |
| |
| // Check result. |
| EXPECT_TRUE(result_proxy_list.has_value()); |
| EXPECT_EQ("PROXY result2:80", result_proxy_list.value()); |
| result_proxy_list.reset(); |
| |
| helper_->ResolveProxy(url3, |
| base::BindLambdaForTesting( |
| [&](const base::Optional<std::string>& proxy_list) { |
| result_proxy_list = proxy_list; |
| })); |
| |
| EXPECT_EQ(url3, helper_->pending_url()); |
| ASSERT_TRUE(proxy_lookup_client_); |
| proxy_info.UseNamedProxy("result3:80"); |
| proxy_lookup_client_->OnProxyLookupComplete(net::OK, proxy_info); |
| base::RunLoop().RunUntilIdle(); |
| |
| // Check result. |
| EXPECT_TRUE(result_proxy_list.has_value()); |
| EXPECT_EQ("PROXY result3:80", result_proxy_list.value()); |
| } |
| |
| // Issue a request while one is already in progress -- should be queued. |
| TEST_F(ResolveProxyHelperTest, QueueRequests) { |
| GURL url1("http://www.google1.com/"); |
| GURL url2("http://www.google2.com/"); |
| GURL url3("http://www.google3.com/"); |
| |
| // Start three requests. All the requests will be pending. |
| base::Optional<std::string> result_proxy_list; |
| helper_->ResolveProxy(url1, |
| base::BindLambdaForTesting( |
| [&](const base::Optional<std::string>& proxy_list) { |
| result_proxy_list = proxy_list; |
| })); |
| helper_->ResolveProxy(url2, |
| base::BindLambdaForTesting( |
| [&](const base::Optional<std::string>& proxy_list) { |
| result_proxy_list = proxy_list; |
| })); |
| helper_->ResolveProxy(url3, |
| base::BindLambdaForTesting( |
| [&](const base::Optional<std::string>& proxy_list) { |
| result_proxy_list = proxy_list; |
| })); |
| |
| // Complete first request. |
| EXPECT_EQ(url1, helper_->pending_url()); |
| ASSERT_TRUE(proxy_lookup_client_); |
| net::ProxyInfo proxy_info; |
| proxy_info.UseNamedProxy("result1:80"); |
| proxy_lookup_client_->OnProxyLookupComplete(net::OK, proxy_info); |
| proxy_lookup_client_.reset(); |
| base::RunLoop().RunUntilIdle(); |
| |
| // Check result. |
| EXPECT_TRUE(result_proxy_list.has_value()); |
| EXPECT_EQ("PROXY result1:80", result_proxy_list.value()); |
| result_proxy_list.reset(); |
| |
| // Complete second request. |
| EXPECT_EQ(url2, helper_->pending_url()); |
| ASSERT_TRUE(proxy_lookup_client_); |
| proxy_info.UseNamedProxy("result2:80"); |
| proxy_lookup_client_->OnProxyLookupComplete(net::OK, proxy_info); |
| proxy_lookup_client_.reset(); |
| base::RunLoop().RunUntilIdle(); |
| |
| // Check result. |
| EXPECT_TRUE(result_proxy_list.has_value()); |
| EXPECT_EQ("PROXY result2:80", result_proxy_list.value()); |
| result_proxy_list.reset(); |
| |
| // Complete third request. |
| EXPECT_EQ(url3, helper_->pending_url()); |
| ASSERT_TRUE(proxy_lookup_client_); |
| proxy_info.UseNamedProxy("result3:80"); |
| proxy_lookup_client_->OnProxyLookupComplete(net::OK, proxy_info); |
| base::RunLoop().RunUntilIdle(); |
| |
| // Check result. |
| EXPECT_TRUE(result_proxy_list.has_value()); |
| EXPECT_EQ("PROXY result3:80", result_proxy_list.value()); |
| } |
| |
| // Delete the helper while a request is in progress and others are pending. |
| TEST_F(ResolveProxyHelperTest, CancelPendingRequests) { |
| GURL url1("http://www.google1.com/"); |
| GURL url2("http://www.google2.com/"); |
| GURL url3("http://www.google3.com/"); |
| |
| // Start three requests. Since the proxy resolver is async, all the |
| // requests will be pending. |
| |
| base::Optional<std::string> result_proxy_list; |
| helper_->ResolveProxy(url1, |
| base::BindLambdaForTesting( |
| [&](const base::Optional<std::string>& proxy_list) { |
| result_proxy_list = proxy_list; |
| })); |
| helper_->ResolveProxy(url2, |
| base::BindLambdaForTesting( |
| [&](const base::Optional<std::string>& proxy_list) { |
| result_proxy_list = proxy_list; |
| })); |
| helper_->ResolveProxy(url3, |
| base::BindLambdaForTesting( |
| [&](const base::Optional<std::string>& proxy_list) { |
| result_proxy_list = proxy_list; |
| })); |
| |
| // Check the first request is pending. |
| EXPECT_EQ(url1, helper_->pending_url()); |
| ASSERT_TRUE(proxy_lookup_client_); |
| |
| // Release a reference. The |helper_| will not be deleted, since there's a |
| // pending resolution. |
| helper_ = nullptr; |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_TRUE(proxy_lookup_client_.is_bound()); |
| EXPECT_FALSE(!proxy_lookup_client_.is_connected()); |
| |
| // Send Mojo message on the pipe. |
| net::ProxyInfo proxy_info; |
| proxy_info.UseNamedProxy("result1:80"); |
| proxy_lookup_client_->OnProxyLookupComplete(net::OK, proxy_info); |
| |
| // Spinning the message loop results in the helper being destroyed and closing |
| // the pipe. |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_TRUE(!proxy_lookup_client_.is_bound() || |
| !proxy_lookup_client_.is_connected()); |
| // The result should not have been sent. |
| EXPECT_FALSE(result_proxy_list.has_value()); |
| |
| // It should also be the case that msg1, msg2, msg3 were deleted by the |
| // cancellation. (Else will show up as a leak). |
| } |
| |
| // Issue a request that fails. |
| TEST_F(ResolveProxyHelperTest, RequestFails) { |
| GURL url("http://www.google.com/"); |
| |
| base::Optional<std::string> result_proxy_list; |
| helper_->ResolveProxy(url, |
| base::BindLambdaForTesting( |
| [&](const base::Optional<std::string>& proxy_list) { |
| result_proxy_list = proxy_list; |
| })); |
| |
| // There should be a pending proxy lookup request. Respond to it. |
| EXPECT_EQ(url, helper_->pending_url()); |
| ASSERT_TRUE(proxy_lookup_client_); |
| proxy_lookup_client_->OnProxyLookupComplete(net::ERR_FAILED, base::nullopt); |
| base::RunLoop().RunUntilIdle(); |
| |
| // Check result. |
| EXPECT_FALSE(result_proxy_list.has_value()); |
| } |
| |
| // Issue a request, only to have the Mojo pipe closed. |
| TEST_F(ResolveProxyHelperTest, PipeClosed) { |
| GURL url("http://www.google.com/"); |
| |
| base::Optional<std::string> result_proxy_list; |
| helper_->ResolveProxy(url, |
| base::BindLambdaForTesting( |
| [&](const base::Optional<std::string>& proxy_list) { |
| result_proxy_list = proxy_list; |
| })); |
| |
| // There should be a pending proxy lookup request. Respond to it by closing |
| // the pipe. |
| EXPECT_EQ(url, helper_->pending_url()); |
| ASSERT_TRUE(proxy_lookup_client_); |
| proxy_lookup_client_.reset(); |
| base::RunLoop().RunUntilIdle(); |
| |
| // Check result. |
| EXPECT_FALSE(result_proxy_list.has_value()); |
| } |
| |
| // Fail to send a request to the network service. |
| TEST_F(ResolveProxyHelperTest, FailToSendRequest) { |
| GURL url("http://www.google.com/"); |
| |
| helper_->set_fail_to_send_request(true); |
| |
| base::Optional<std::string> result_proxy_list; |
| helper_->ResolveProxy(url, |
| base::BindLambdaForTesting( |
| [&](const base::Optional<std::string>& proxy_list) { |
| result_proxy_list = proxy_list; |
| })); |
| // No request should be pending. |
| EXPECT_TRUE(helper_->pending_url().is_empty()); |
| |
| // Check result. |
| EXPECT_FALSE(result_proxy_list.has_value()); |
| } |
| |
| // Make sure if mojo callback is invoked after last externally owned reference |
| // is released, there is no crash. |
| // Regression test for https://crbug.com/870675 |
| TEST_F(ResolveProxyHelperTest, Lifetime) { |
| GURL url("http://www.google1.com/"); |
| |
| base::Optional<std::string> result_proxy_list; |
| helper_->ResolveProxy(url, |
| base::BindLambdaForTesting( |
| [&](const base::Optional<std::string>& proxy_list) { |
| result_proxy_list = proxy_list; |
| })); |
| |
| // There should be a pending proxy lookup request. Respond to it. |
| EXPECT_EQ(url, helper_->pending_url()); |
| ASSERT_TRUE(proxy_lookup_client_); |
| |
| // Release the |helper_| pointer. The object should keep a reference to |
| // itself, so should not be deleted. |
| helper_ = nullptr; |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_TRUE(proxy_lookup_client_.is_bound()); |
| EXPECT_FALSE(!proxy_lookup_client_.is_connected()); |
| |
| // Send Mojo message on the pipe. |
| net::ProxyInfo proxy_info; |
| proxy_info.UseNamedProxy("result1:80"); |
| proxy_lookup_client_->OnProxyLookupComplete(net::OK, proxy_info); |
| |
| // Spinning the message loop results in the helper being destroyed and closing |
| // the pipe. |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_TRUE(!proxy_lookup_client_.is_bound() || |
| !proxy_lookup_client_.is_connected()); |
| // The result should not have been sent. |
| EXPECT_FALSE(result_proxy_list.has_value()); |
| } |
| |
| } // namespace content |