// Copyright 2015 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 "services/proxy_resolver/proxy_resolver_factory_impl.h" #include #include "base/bind.h" #include "base/callback_helpers.h" #include "base/memory/raw_ptr.h" #include "base/run_loop.h" #include "base/strings/utf_string_conversions.h" #include "base/test/task_environment.h" #include "mojo/public/cpp/bindings/receiver.h" #include "mojo/public/cpp/bindings/remote.h" #include "net/base/completion_once_callback.h" #include "net/base/test_completion_callback.h" #include "net/proxy_resolution/mock_proxy_resolver.h" #include "net/proxy_resolution/proxy_resolve_dns_operation.h" #include "net/test/event_waiter.h" #include "net/test/gtest_util.h" #include "services/proxy_resolver/proxy_resolver_v8_tracing.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/abseil-cpp/absl/types/optional.h" using net::test::IsError; using net::test::IsOk; namespace proxy_resolver { namespace { const char kScriptData[] = "FooBarBaz"; const char16_t kScriptData16[] = u"FooBarBaz"; class FakeProxyResolver : public ProxyResolverV8Tracing { public: explicit FakeProxyResolver(base::OnceClosure on_destruction) : on_destruction_(std::move(on_destruction)) {} ~FakeProxyResolver() override { std::move(on_destruction_).Run(); } private: // ProxyResolverV8Tracing overrides. void GetProxyForURL(const GURL& url, const net::NetworkIsolationKey& network_isolation_key, net::ProxyInfo* results, net::CompletionOnceCallback callback, std::unique_ptr* request, std::unique_ptr bindings) override {} base::OnceClosure on_destruction_; }; enum Event { NONE, RESOLVER_CREATED, CONNECTION_ERROR, RESOLVER_DESTROYED, }; class TestProxyResolverFactory : public ProxyResolverV8TracingFactory { public: struct PendingRequest { raw_ptr> resolver; net::CompletionOnceCallback callback; }; explicit TestProxyResolverFactory(net::EventWaiter* waiter) : waiter_(waiter) {} ~TestProxyResolverFactory() override {} void CreateProxyResolverV8Tracing( const scoped_refptr& pac_script, std::unique_ptr bindings, std::unique_ptr* resolver, net::CompletionOnceCallback callback, std::unique_ptr* request) override { requests_handled_++; waiter_->NotifyEvent(RESOLVER_CREATED); EXPECT_EQ(kScriptData16, pac_script->utf16()); EXPECT_TRUE(resolver); pending_request_ = std::make_unique(); pending_request_->resolver = resolver; pending_request_->callback = std::move(callback); ASSERT_TRUE(bindings); bindings->Alert(u"alert"); bindings->OnError(10, u"error"); EXPECT_TRUE(bindings->GetHostResolver()); } size_t requests_handled() { return requests_handled_; } PendingRequest* pending_request() { return pending_request_.get(); } private: raw_ptr> waiter_; size_t requests_handled_ = 0; std::unique_ptr pending_request_; }; class TestProxyResolverFactoryImpl : public ProxyResolverFactoryImpl { public: TestProxyResolverFactoryImpl( mojo::PendingReceiver receiver, std::unique_ptr factory) : ProxyResolverFactoryImpl(std::move(receiver), std::move(factory)) {} }; } // namespace class ProxyResolverFactoryImplTest : public testing::Test, public mojom::ProxyResolverFactoryRequestClient { public: ProxyResolverFactoryImplTest() { std::unique_ptr test_factory = std::make_unique(&waiter_); mock_factory_ = test_factory.get(); mock_factory_impl_ = std::make_unique( factory_.BindNewPipeAndPassReceiver(), std::move(test_factory)); factory_.set_idle_handler( base::TimeDelta(), base::BindRepeating(&ProxyResolverFactoryImplTest::OnFactoryIdle, base::Unretained(this))); } ~ProxyResolverFactoryImplTest() override = default; void OnDisconnect() { waiter_.NotifyEvent(CONNECTION_ERROR); } void OnFakeProxyInstanceDestroyed() { instances_destroyed_++; waiter_.NotifyEvent(RESOLVER_DESTROYED); } void ReportResult(int32_t error) override { std::move(create_callback_).Run(error); } void Alert(const std::string& message) override {} void OnError(int32_t line_number, const std::string& message) override {} void ResolveDns( const std::string& hostname, net::ProxyResolveDnsOperation operation, const net::NetworkIsolationKey& network_isolation_key, mojo::PendingRemote client) override {} void set_idle_callback(base::OnceClosure callback) { idle_callback_ = std::move(callback); } protected: void OnFactoryIdle() { if (idle_callback_) std::move(idle_callback_).Run(); } base::test::TaskEnvironment task_environment_; std::unique_ptr mock_factory_impl_; raw_ptr mock_factory_; mojo::Remote factory_; int instances_destroyed_ = 0; net::CompletionOnceCallback create_callback_; base::OnceClosure idle_callback_; net::EventWaiter waiter_; }; TEST_F(ProxyResolverFactoryImplTest, DisconnectProxyResolverClient) { mojo::Remote proxy_resolver; mojo::PendingRemote client; mojo::Receiver client_receiver( this, client.InitWithNewPipeAndPassReceiver()); factory_->CreateResolver(kScriptData, proxy_resolver.BindNewPipeAndPassReceiver(), std::move(client)); proxy_resolver.set_disconnect_handler(base::BindOnce( &ProxyResolverFactoryImplTest::OnDisconnect, base::Unretained(this))); waiter_.WaitForEvent(RESOLVER_CREATED); EXPECT_EQ(0, instances_destroyed_); ASSERT_EQ(1u, mock_factory_->requests_handled()); net::TestCompletionCallback create_callback; create_callback_ = create_callback.callback(); ASSERT_TRUE(mock_factory_->pending_request()); *mock_factory_->pending_request()->resolver = std::make_unique(base::BindOnce( &ProxyResolverFactoryImplTest::OnFakeProxyInstanceDestroyed, base::Unretained(this))); std::move(mock_factory_->pending_request()->callback).Run(net::OK); EXPECT_THAT(create_callback.WaitForResult(), IsOk()); base::RunLoop wait_for_idle_loop; set_idle_callback(wait_for_idle_loop.QuitClosure()); proxy_resolver.reset(); waiter_.WaitForEvent(RESOLVER_DESTROYED); EXPECT_EQ(1, instances_destroyed_); wait_for_idle_loop.Run(); } // Same as above, but disconnect the factory right after the CreateResolver // call, which should not prevent the request from succeeding. TEST_F(ProxyResolverFactoryImplTest, DisconnectProxyResolverFactory) { mojo::Remote proxy_resolver; mojo::PendingRemote client; mojo::Receiver client_receiver( this, client.InitWithNewPipeAndPassReceiver()); factory_->CreateResolver(kScriptData, proxy_resolver.BindNewPipeAndPassReceiver(), std::move(client)); proxy_resolver.set_disconnect_handler(base::BindOnce( &ProxyResolverFactoryImplTest::OnDisconnect, base::Unretained(this))); waiter_.WaitForEvent(RESOLVER_CREATED); EXPECT_EQ(0, instances_destroyed_); ASSERT_EQ(1u, mock_factory_->requests_handled()); net::TestCompletionCallback create_callback; create_callback_ = create_callback.callback(); ASSERT_TRUE(mock_factory_->pending_request()); *mock_factory_->pending_request()->resolver = std::make_unique(base::BindOnce( &ProxyResolverFactoryImplTest::OnFakeProxyInstanceDestroyed, base::Unretained(this))); std::move(mock_factory_->pending_request()->callback).Run(net::OK); EXPECT_THAT(create_callback.WaitForResult(), IsOk()); base::RunLoop wait_for_idle_loop; set_idle_callback(wait_for_idle_loop.QuitClosure()); proxy_resolver.reset(); waiter_.WaitForEvent(RESOLVER_DESTROYED); EXPECT_EQ(1, instances_destroyed_); wait_for_idle_loop.Run(); } TEST_F(ProxyResolverFactoryImplTest, Error) { mojo::Remote proxy_resolver; mojo::PendingRemote client; mojo::Receiver client_receiver( this, client.InitWithNewPipeAndPassReceiver()); factory_->CreateResolver(kScriptData, proxy_resolver.BindNewPipeAndPassReceiver(), std::move(client)); proxy_resolver.set_disconnect_handler(base::BindOnce( &ProxyResolverFactoryImplTest::OnDisconnect, base::Unretained(this))); waiter_.WaitForEvent(RESOLVER_CREATED); EXPECT_EQ(0, instances_destroyed_); ASSERT_EQ(1u, mock_factory_->requests_handled()); net::TestCompletionCallback create_callback; create_callback_ = create_callback.callback(); ASSERT_TRUE(mock_factory_->pending_request()); std::move(mock_factory_->pending_request()->callback) .Run(net::ERR_PAC_SCRIPT_FAILED); EXPECT_THAT(create_callback.WaitForResult(), IsError(net::ERR_PAC_SCRIPT_FAILED)); } TEST_F(ProxyResolverFactoryImplTest, DisconnectClientDuringResolverCreation) { mojo::Remote proxy_resolver; mojo::PendingRemote client; mojo::Receiver client_receiver( this, client.InitWithNewPipeAndPassReceiver()); factory_->CreateResolver(kScriptData, proxy_resolver.BindNewPipeAndPassReceiver(), std::move(client)); proxy_resolver.set_disconnect_handler(base::BindOnce( &ProxyResolverFactoryImplTest::OnDisconnect, base::Unretained(this))); waiter_.WaitForEvent(RESOLVER_CREATED); EXPECT_EQ(0, instances_destroyed_); ASSERT_EQ(1u, mock_factory_->requests_handled()); client_receiver.reset(); waiter_.WaitForEvent(CONNECTION_ERROR); } } // namespace proxy_resolver