blob: 2719c3b63c3e5f36646ab458cf2b9919c8662d00 [file] [log] [blame]
// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/net/chrome_mojo_proxy_resolver_factory.h"
#include <stdint.h>
#include <optional>
#include <vector>
#include "base/process/process.h"
#include "base/run_loop.h"
#include "base/synchronization/waitable_event.h"
#include "base/test/bind.h"
#include "base/time/time.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/service_process_host.h"
#include "content/public/test/browser_test.h"
#include "mojo/public/cpp/bindings/receiver.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "services/proxy_resolver/public/mojom/proxy_resolver.mojom.h"
namespace {
constexpr base::TimeDelta kServiceShutdownTimeout = base::Seconds(3);
constexpr char kPacScript[] =
"function FindProxyForURL(url, host) { return 'PROXY proxy.example.com:1; "
"DIRECT'; }";
// Dummy consumer of a ProxyResolverFactory. It just calls CreateResolver, and
// keeps Mojo objects alive from when CreateResolver() is called until it's
// destroyed.
class DumbProxyResolverFactoryRequestClient
: public proxy_resolver::mojom::ProxyResolverFactoryRequestClient {
public:
DumbProxyResolverFactoryRequestClient() = default;
~DumbProxyResolverFactoryRequestClient() override {
EXPECT_TRUE(receiver_.is_bound());
}
void CreateResolver(
proxy_resolver::mojom::ProxyResolverFactory* resolver_factory) {
mojo::PendingRemote<
proxy_resolver::mojom::ProxyResolverFactoryRequestClient>
resolver_client;
receiver_.Bind(resolver_client.InitWithNewPipeAndPassReceiver());
resolver_factory->CreateResolver(kPacScript,
resolver_.BindNewPipeAndPassReceiver(),
std::move(resolver_client));
// Wait for proxy resolver to be created, to avoid any races between
// creating one resolver and destroying the next one.
run_loop_.Run();
}
private:
// ProxyResolverFactoryRequestClient implementation:
void ReportResult(int32_t error) override {
EXPECT_EQ(net::OK, error);
run_loop_.Quit();
}
void Alert(const std::string& error) override {}
void OnError(int32_t line_number, const std::string& error) override {}
void ResolveDns(
const std::string& hostname,
net::ProxyResolveDnsOperation operation,
const net::NetworkAnonymizationKey& network_anonymization_key,
mojo::PendingRemote<proxy_resolver::mojom::HostResolverRequestClient>
client) override {}
mojo::Remote<proxy_resolver::mojom::ProxyResolver> resolver_;
mojo::Receiver<proxy_resolver::mojom::ProxyResolverFactoryRequestClient>
receiver_{this};
base::RunLoop run_loop_;
};
using ChromeMojoProxyResolverFactoryBrowserTest = InProcessBrowserTest;
class ProxyResolverProcessObserver
: public content::ServiceProcessHost::Observer {
public:
ProxyResolverProcessObserver() {
content::ServiceProcessHost::AddObserver(this);
}
ProxyResolverProcessObserver(const ProxyResolverProcessObserver&) = delete;
ProxyResolverProcessObserver& operator=(const ProxyResolverProcessObserver&) =
delete;
~ProxyResolverProcessObserver() override {
content::ServiceProcessHost::RemoveObserver(this);
}
bool is_service_running() const { return is_service_running_; }
void WaitForLaunch() { launch_loop_.Run(); }
void WaitForDeath() { death_loop_.Run(); }
private:
// content::ServiceProcessHost::Observer:
void OnServiceProcessLaunched(
const content::ServiceProcessInfo& info) override {
if (!info.IsService<proxy_resolver::mojom::ProxyResolverFactory>())
return;
ASSERT_FALSE(is_service_running_);
is_service_running_ = true;
launch_loop_.Quit();
}
void OnServiceProcessTerminatedNormally(
const content::ServiceProcessInfo& info) override {
if (!info.IsService<proxy_resolver::mojom::ProxyResolverFactory>())
return;
ASSERT_TRUE(is_service_running_);
is_service_running_ = false;
death_loop_.Quit();
}
private:
bool is_service_running_ = false;
base::RunLoop launch_loop_;
base::RunLoop death_loop_;
};
// Ensures the proxy resolver service is started correctly and stopped when no
// resolvers are open.
IN_PROC_BROWSER_TEST_F(ChromeMojoProxyResolverFactoryBrowserTest,
ServiceLifecycle) {
// Set up the ProxyResolverFactory.
mojo::Remote<proxy_resolver::mojom::ProxyResolverFactory> resolver_factory(
ChromeMojoProxyResolverFactory::CreateWithSelfOwnedReceiver());
ProxyResolverProcessObserver observer;
// Create a resolver, this should create and start the service.
std::unique_ptr<DumbProxyResolverFactoryRequestClient> resolver_client1 =
std::make_unique<DumbProxyResolverFactoryRequestClient>();
resolver_client1->CreateResolver(resolver_factory.get());
observer.WaitForLaunch();
// Create another resolver, no new service should be created (the listener
// will assert if that's the case).
std::unique_ptr<DumbProxyResolverFactoryRequestClient> resolver_client2 =
std::make_unique<DumbProxyResolverFactoryRequestClient>();
resolver_client2->CreateResolver(resolver_factory.get());
// Close one resolver.
resolver_client1.reset();
// The service should not be stopped as there is another resolver.
// Wait a little bit and check it's still running.
{
base::RunLoop run_loop;
content::GetUIThreadTaskRunner({})->PostDelayedTask(
FROM_HERE, run_loop.QuitClosure(), kServiceShutdownTimeout);
run_loop.Run();
}
EXPECT_TRUE(observer.is_service_running());
// Close the last resolver, the service should now go away.
resolver_client2.reset();
observer.WaitForDeath();
}
// Same as above, but destroys the ProxyResolverFactory, which should have no
// impact on resolver lifetime.
IN_PROC_BROWSER_TEST_F(ChromeMojoProxyResolverFactoryBrowserTest,
DestroyFactory) {
// Set up the ProxyResolverFactory.
mojo::Remote<proxy_resolver::mojom::ProxyResolverFactory> resolver_factory(
ChromeMojoProxyResolverFactory::CreateWithSelfOwnedReceiver());
ProxyResolverProcessObserver observer;
// Create a resolver, this should create and start the service.
std::unique_ptr<DumbProxyResolverFactoryRequestClient> resolver_client1 =
std::make_unique<DumbProxyResolverFactoryRequestClient>();
resolver_client1->CreateResolver(resolver_factory.get());
observer.WaitForLaunch();
// Create another resolver, no new service should be created (the listener
// will assert if that's the case).
std::unique_ptr<DumbProxyResolverFactoryRequestClient> resolver_client2 =
std::make_unique<DumbProxyResolverFactoryRequestClient>();
resolver_client2->CreateResolver(resolver_factory.get());
// Destroy the factory. Should not result in the resolver being destroyed.
resolver_factory.reset();
// Close one resolver.
resolver_client1.reset();
// The service should not be stopped as there is another resolver.
// Wait a little bit and check it's still running.
{
base::RunLoop run_loop;
content::GetUIThreadTaskRunner({})->PostDelayedTask(
FROM_HERE, run_loop.QuitClosure(), kServiceShutdownTimeout);
run_loop.Run();
}
EXPECT_TRUE(observer.is_service_running());
// Close the last resolver, the service should now go away.
resolver_client2.reset();
observer.WaitForDeath();
}
// Make sure the service can be started again after it's been stopped.
IN_PROC_BROWSER_TEST_F(ChromeMojoProxyResolverFactoryBrowserTest,
DestroyAndCreateService) {
// Set up the ProxyResolverFactory.
mojo::Remote<proxy_resolver::mojom::ProxyResolverFactory> resolver_factory(
ChromeMojoProxyResolverFactory::CreateWithSelfOwnedReceiver());
std::optional<ProxyResolverProcessObserver> observer{std::in_place};
// Create a resolver, this should create and start the service.
std::unique_ptr<DumbProxyResolverFactoryRequestClient> resolver_client =
std::make_unique<DumbProxyResolverFactoryRequestClient>();
resolver_client->CreateResolver(resolver_factory.get());
observer->WaitForLaunch();
// Close the resolver, the service should stop.
resolver_client.reset();
observer->WaitForDeath();
observer.emplace();
// Create a resolver again, using the same factory. This should create and
// start the service.
resolver_client = std::make_unique<DumbProxyResolverFactoryRequestClient>();
resolver_client->CreateResolver(resolver_factory.get());
observer->WaitForLaunch();
// Close the resolver again, the service should stop.
resolver_client.reset();
observer->WaitForDeath();
}
} // namespace