blob: 7f8765fd434fdbb973fbb1f5678e4c42fb013cc0 [file] [log] [blame]
// 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 <utility>
#include "base/bind.h"
#include "base/run_loop.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/scoped_task_environment.h"
#include "mojo/public/cpp/bindings/binding.h"
#include "mojo/public/cpp/bindings/strong_binding.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/proxy_resolution/proxy_resolver_v8_tracing.h"
#include "net/test/event_waiter.h"
#include "net/test/gtest_util.h"
#include "services/service_manager/public/cpp/service_keepalive.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using net::test::IsError;
using net::test::IsOk;
namespace proxy_resolver {
namespace {
const char kScriptData[] = "FooBarBaz";
class FakeProxyResolver : public net::ProxyResolverV8Tracing {
public:
explicit FakeProxyResolver(const base::Closure& on_destruction)
: on_destruction_(on_destruction) {}
~FakeProxyResolver() override { on_destruction_.Run(); }
private:
// net::ProxyResolverV8Tracing overrides.
void GetProxyForURL(const GURL& url,
net::ProxyInfo* results,
net::CompletionOnceCallback callback,
std::unique_ptr<net::ProxyResolver::Request>* request,
std::unique_ptr<Bindings> bindings) override {}
const base::Closure on_destruction_;
};
enum Event {
NONE,
RESOLVER_CREATED,
CONNECTION_ERROR,
RESOLVER_DESTROYED,
};
class TestProxyResolverFactory : public net::ProxyResolverV8TracingFactory {
public:
struct PendingRequest {
std::unique_ptr<net::ProxyResolverV8Tracing>* resolver;
net::CompletionOnceCallback callback;
};
explicit TestProxyResolverFactory(net::EventWaiter<Event>* waiter)
: waiter_(waiter) {}
~TestProxyResolverFactory() override {}
void CreateProxyResolverV8Tracing(
const scoped_refptr<net::PacFileData>& pac_script,
std::unique_ptr<net::ProxyResolverV8Tracing::Bindings> bindings,
std::unique_ptr<net::ProxyResolverV8Tracing>* resolver,
net::CompletionOnceCallback callback,
std::unique_ptr<net::ProxyResolverFactory::Request>* request) override {
requests_handled_++;
waiter_->NotifyEvent(RESOLVER_CREATED);
EXPECT_EQ(base::ASCIIToUTF16(kScriptData), pac_script->utf16());
EXPECT_TRUE(resolver);
pending_request_.reset(new PendingRequest);
pending_request_->resolver = resolver;
pending_request_->callback = std::move(callback);
ASSERT_TRUE(bindings);
bindings->Alert(base::ASCIIToUTF16("alert"));
bindings->OnError(10, base::ASCIIToUTF16("error"));
EXPECT_TRUE(bindings->GetHostResolver());
}
size_t requests_handled() { return requests_handled_; }
PendingRequest* pending_request() { return pending_request_.get(); }
private:
net::EventWaiter<Event>* waiter_;
size_t requests_handled_ = 0;
std::unique_ptr<PendingRequest> pending_request_;
};
class TestProxyResolverFactoryImpl : public ProxyResolverFactoryImpl {
public:
explicit TestProxyResolverFactoryImpl(
std::unique_ptr<net::ProxyResolverV8TracingFactory>
proxy_resolver_factory)
: ProxyResolverFactoryImpl(std::move(proxy_resolver_factory)) {}
};
} // namespace
class ProxyResolverFactoryImplTest
: public testing::Test,
public mojom::ProxyResolverFactoryRequestClient,
public service_manager::ServiceKeepalive::Observer {
public:
ProxyResolverFactoryImplTest()
: service_keepalive_(
static_cast<service_manager::ServiceBinding*>(nullptr),
base::TimeDelta()) {
service_keepalive_.AddObserver(this);
std::unique_ptr<TestProxyResolverFactory> test_factory =
std::make_unique<TestProxyResolverFactory>(&waiter_);
mock_factory_ = test_factory.get();
mock_factory_impl_ =
std::make_unique<TestProxyResolverFactoryImpl>(std::move(test_factory));
mock_factory_impl_->BindRequest(mojo::MakeRequest(&factory_),
&service_keepalive_);
}
~ProxyResolverFactoryImplTest() override {
service_keepalive_.RemoveObserver(this);
}
void OnConnectionError() { 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,
mojom::HostResolverRequestClientPtr client) override {}
void WaitForNoServiceRefs() {
DCHECK(!service_keepalive_ref_run_loop_);
if (service_keepalive_.HasNoRefs())
return;
service_keepalive_ref_run_loop_ = std::make_unique<base::RunLoop>();
service_keepalive_ref_run_loop_->Run();
service_keepalive_ref_run_loop_.reset();
EXPECT_TRUE(service_keepalive_.HasNoRefs());
}
protected:
// service_manager::ServiceKeepalive::Observer:
void OnIdleTimeout() override {
if (service_keepalive_ref_run_loop_)
service_keepalive_ref_run_loop_->Quit();
}
base::test::ScopedTaskEnvironment task_environment_;
std::unique_ptr<TestProxyResolverFactoryImpl> mock_factory_impl_;
TestProxyResolverFactory* mock_factory_;
mojom::ProxyResolverFactoryPtr factory_;
service_manager::ServiceKeepalive service_keepalive_;
std::unique_ptr<base::RunLoop> service_keepalive_ref_run_loop_;
int instances_destroyed_ = 0;
net::CompletionOnceCallback create_callback_;
net::EventWaiter<Event> waiter_;
};
TEST_F(ProxyResolverFactoryImplTest, DisconnectProxyResolverClient) {
mojom::ProxyResolverPtr proxy_resolver;
mojom::ProxyResolverFactoryRequestClientPtr client_ptr;
mojo::Binding<ProxyResolverFactoryRequestClient> client_binding(
this, mojo::MakeRequest(&client_ptr));
factory_->CreateResolver(kScriptData, mojo::MakeRequest(&proxy_resolver),
std::move(client_ptr));
proxy_resolver.set_connection_error_handler(
base::Bind(&ProxyResolverFactoryImplTest::OnConnectionError,
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->reset(new FakeProxyResolver(
base::Bind(&ProxyResolverFactoryImplTest::OnFakeProxyInstanceDestroyed,
base::Unretained(this))));
std::move(mock_factory_->pending_request()->callback).Run(net::OK);
EXPECT_THAT(create_callback.WaitForResult(), IsOk());
EXPECT_FALSE(service_keepalive_.HasNoRefs());
proxy_resolver.reset();
waiter_.WaitForEvent(RESOLVER_DESTROYED);
EXPECT_EQ(1, instances_destroyed_);
EXPECT_FALSE(service_keepalive_.HasNoRefs());
task_environment_.RunUntilIdle();
EXPECT_FALSE(service_keepalive_.HasNoRefs());
factory_.reset();
WaitForNoServiceRefs();
}
// Same as above, but disconnect the factory right after the CreateResolver
// call, which should not prevent the request from succeeding.
TEST_F(ProxyResolverFactoryImplTest, DisconnectProxyResolverFactory) {
mojom::ProxyResolverPtr proxy_resolver;
mojom::ProxyResolverFactoryRequestClientPtr client_ptr;
mojo::Binding<ProxyResolverFactoryRequestClient> client_binding(
this, mojo::MakeRequest(&client_ptr));
factory_->CreateResolver(kScriptData, mojo::MakeRequest(&proxy_resolver),
std::move(client_ptr));
factory_.reset();
proxy_resolver.set_connection_error_handler(
base::Bind(&ProxyResolverFactoryImplTest::OnConnectionError,
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->reset(new FakeProxyResolver(
base::Bind(&ProxyResolverFactoryImplTest::OnFakeProxyInstanceDestroyed,
base::Unretained(this))));
std::move(mock_factory_->pending_request()->callback).Run(net::OK);
EXPECT_THAT(create_callback.WaitForResult(), IsOk());
EXPECT_FALSE(service_keepalive_.HasNoRefs());
task_environment_.RunUntilIdle();
EXPECT_FALSE(service_keepalive_.HasNoRefs());
proxy_resolver.reset();
waiter_.WaitForEvent(RESOLVER_DESTROYED);
EXPECT_EQ(1, instances_destroyed_);
WaitForNoServiceRefs();
}
TEST_F(ProxyResolverFactoryImplTest, Error) {
mojom::ProxyResolverPtr proxy_resolver;
mojom::ProxyResolverFactoryRequestClientPtr client_ptr;
mojo::Binding<ProxyResolverFactoryRequestClient> client_binding(
this, mojo::MakeRequest(&client_ptr));
factory_->CreateResolver(kScriptData, mojo::MakeRequest(&proxy_resolver),
std::move(client_ptr));
proxy_resolver.set_connection_error_handler(
base::Bind(&ProxyResolverFactoryImplTest::OnConnectionError,
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) {
mojom::ProxyResolverPtr proxy_resolver;
mojom::ProxyResolverFactoryRequestClientPtr client_ptr;
mojo::Binding<ProxyResolverFactoryRequestClient> client_binding(
this, mojo::MakeRequest(&client_ptr));
factory_->CreateResolver(kScriptData, mojo::MakeRequest(&proxy_resolver),
std::move(client_ptr));
proxy_resolver.set_connection_error_handler(
base::Bind(&ProxyResolverFactoryImplTest::OnConnectionError,
base::Unretained(this)));
waiter_.WaitForEvent(RESOLVER_CREATED);
EXPECT_EQ(0, instances_destroyed_);
ASSERT_EQ(1u, mock_factory_->requests_handled());
client_binding.Close();
waiter_.WaitForEvent(CONNECTION_ERROR);
}
TEST_F(ProxyResolverFactoryImplTest, MultipleFactories) {
// Creating |factory_| should have resulted in an outstanding service
// reference.
EXPECT_FALSE(service_keepalive_.HasNoRefs());
// Creating another shouldn't change that.
mojom::ProxyResolverFactoryPtr factory2;
mock_factory_impl_->BindRequest(mojo::MakeRequest(&factory2),
&service_keepalive_);
EXPECT_FALSE(service_keepalive_.HasNoRefs());
// Destroying one factory while keeping the other around should not release
// the reference.
factory_.reset();
task_environment_.RunUntilIdle();
EXPECT_FALSE(service_keepalive_.HasNoRefs());
// Destroying the second factory should release the reference.
factory2.reset();
WaitForNoServiceRefs();
// Test that creating and then destroying a new factory gets and releases a
// reference again.
mock_factory_impl_->BindRequest(mojo::MakeRequest(&factory2),
&service_keepalive_);
EXPECT_FALSE(service_keepalive_.HasNoRefs());
factory2.reset();
WaitForNoServiceRefs();
}
} // namespace proxy_resolver