blob: 75d50756493ccec2a3507364efc242bc08757b1b [file] [log] [blame]
// Copyright 2017 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 <memory>
#include <utility>
#include "base/optional.h"
#include "base/run_loop.h"
#include "base/strings/string_util.h"
#include "base/test/scoped_task_environment.h"
#include "base/threading/thread_task_runner_handle.h"
#include "build/build_config.h"
#include "net/base/mock_network_change_notifier.h"
#include "net/dns/dns_config_service.h"
#include "net/dns/host_resolver.h"
#include "net/http/http_auth_handler_factory.h"
#include "net/http/http_auth_scheme.h"
#include "net/net_buildflags.h"
#include "net/proxy_resolution/proxy_config.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "net/test/spawned_test_server/spawned_test_server.h"
#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
#include "net/url_request/url_request_context.h"
#include "services/network/network_context.h"
#include "services/network/network_service.h"
#include "services/network/public/mojom/network_change_manager.mojom.h"
#include "services/network/public/mojom/network_service.mojom.h"
#include "services/network/test/test_url_loader_client.h"
#include "services/service_manager/public/cpp/service_context.h"
#include "services/service_manager/public/cpp/service_test.h"
#include "services/service_manager/public/mojom/service_factory.mojom.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
#if BUILDFLAG(USE_KERBEROS)
#include "net/http/http_auth_handler_negotiate.h"
#endif
namespace network {
namespace {
const char kNetworkServiceName[] = "network";
const base::FilePath::CharType kServicesTestData[] =
FILE_PATH_LITERAL("services/test/data");
mojom::NetworkContextParamsPtr CreateContextParams() {
mojom::NetworkContextParamsPtr params = mojom::NetworkContextParams::New();
// Use a fixed proxy config, to avoid dependencies on local network
// configuration.
params->initial_proxy_config = net::ProxyConfigWithAnnotation::CreateDirect();
return params;
}
class NetworkServiceTest : public testing::Test {
public:
NetworkServiceTest()
: scoped_task_environment_(
base::test::ScopedTaskEnvironment::MainThreadType::IO),
service_(NetworkService::CreateForTesting()) {}
~NetworkServiceTest() override {}
NetworkService* service() const { return service_.get(); }
void DestroyService() { service_.reset(); }
private:
base::test::ScopedTaskEnvironment scoped_task_environment_;
std::unique_ptr<NetworkService> service_;
};
// Test shutdown in the case a NetworkContext is destroyed before the
// NetworkService.
TEST_F(NetworkServiceTest, CreateAndDestroyContext) {
mojom::NetworkContextPtr network_context;
service()->CreateNetworkContext(mojo::MakeRequest(&network_context),
CreateContextParams());
network_context.reset();
// Make sure the NetworkContext is destroyed.
base::RunLoop().RunUntilIdle();
}
// Test shutdown in the case there is still a live NetworkContext when the
// NetworkService is destroyed. The service should destroy the NetworkContext
// itself.
TEST_F(NetworkServiceTest, DestroyingServiceDestroysContext) {
mojom::NetworkContextPtr network_context;
service()->CreateNetworkContext(mojo::MakeRequest(&network_context),
CreateContextParams());
base::RunLoop run_loop;
network_context.set_connection_error_handler(run_loop.QuitClosure());
DestroyService();
// Destroying the service should destroy the context, causing a connection
// error.
run_loop.Run();
}
TEST_F(NetworkServiceTest, CreateContextWithoutChannelID) {
mojom::NetworkContextParamsPtr params = CreateContextParams();
params->cookie_path = base::FilePath();
params->enable_encrypted_cookies = false;
mojom::NetworkContextPtr network_context;
service()->CreateNetworkContext(mojo::MakeRequest(&network_context),
std::move(params));
network_context.reset();
// Make sure the NetworkContext is destroyed.
base::RunLoop().RunUntilIdle();
}
// Platforms where Negotiate can be used.
#if BUILDFLAG(USE_KERBEROS) && !defined(OS_ANDROID)
// Returns the negotiate factory, if one exists, to query its configuration.
net::HttpAuthHandlerNegotiate::Factory* GetNegotiateFactory(
NetworkContext* network_context) {
net::HttpAuthHandlerFactory* auth_factory =
network_context->url_request_context()->http_auth_handler_factory();
return reinterpret_cast<net::HttpAuthHandlerNegotiate::Factory*>(
reinterpret_cast<net::HttpAuthHandlerRegistryFactory*>(auth_factory)
->GetSchemeFactory(net::kNegotiateAuthScheme));
}
#endif // BUILDFLAG(USE_KERBEROS)
TEST_F(NetworkServiceTest, AuthDefaultParams) {
mojom::NetworkContextPtr network_context_ptr;
NetworkContext network_context(service(),
mojo::MakeRequest(&network_context_ptr),
CreateContextParams());
net::HttpAuthHandlerRegistryFactory* auth_handler_factory =
reinterpret_cast<net::HttpAuthHandlerRegistryFactory*>(
network_context.url_request_context()->http_auth_handler_factory());
ASSERT_TRUE(auth_handler_factory);
// These three factories should always be created by default. Negotiate may
// or may not be created, depending on other build flags.
EXPECT_TRUE(auth_handler_factory->GetSchemeFactory(net::kBasicAuthScheme));
EXPECT_TRUE(auth_handler_factory->GetSchemeFactory(net::kDigestAuthScheme));
EXPECT_TRUE(auth_handler_factory->GetSchemeFactory(net::kNtlmAuthScheme));
#if BUILDFLAG(USE_KERBEROS) && !defined(OS_ANDROID)
ASSERT_TRUE(GetNegotiateFactory(&network_context));
#if defined(OS_CHROMEOS)
EXPECT_TRUE(GetNegotiateFactory(&network_context)
->allow_gssapi_library_load_for_testing());
#elif defined(OS_POSIX)
EXPECT_EQ("",
GetNegotiateFactory(&network_context)->GetLibraryNameForTesting());
#endif
#endif // BUILDFLAG(USE_KERBEROS) && !defined(OS_ANDROID)
EXPECT_FALSE(auth_handler_factory->http_auth_preferences()
->NegotiateDisableCnameLookup());
EXPECT_FALSE(
auth_handler_factory->http_auth_preferences()->NegotiateEnablePort());
#if defined(OS_POSIX) || defined(OS_FUCHSIA)
EXPECT_TRUE(auth_handler_factory->http_auth_preferences()->NtlmV2Enabled());
#endif // defined(OS_POSIX) || defined(OS_FUCHSIA)
#if defined(OS_ANDROID)
EXPECT_EQ("", auth_handler_factory->http_auth_preferences()
->AuthAndroidNegotiateAccountType());
#endif // defined(OS_ANDROID)
}
TEST_F(NetworkServiceTest, AuthSchemesDigestAndNtlmOnly) {
mojom::HttpAuthStaticParamsPtr auth_params =
mojom::HttpAuthStaticParams::New();
auth_params->supported_schemes.push_back("digest");
auth_params->supported_schemes.push_back("ntlm");
service()->SetUpHttpAuth(std::move(auth_params));
mojom::NetworkContextPtr network_context_ptr;
NetworkContext network_context(service(),
mojo::MakeRequest(&network_context_ptr),
CreateContextParams());
net::HttpAuthHandlerRegistryFactory* auth_handler_factory =
reinterpret_cast<net::HttpAuthHandlerRegistryFactory*>(
network_context.url_request_context()->http_auth_handler_factory());
ASSERT_TRUE(auth_handler_factory);
EXPECT_FALSE(auth_handler_factory->GetSchemeFactory(net::kBasicAuthScheme));
EXPECT_TRUE(auth_handler_factory->GetSchemeFactory(net::kDigestAuthScheme));
EXPECT_TRUE(auth_handler_factory->GetSchemeFactory(net::kNtlmAuthScheme));
EXPECT_FALSE(
auth_handler_factory->GetSchemeFactory(net::kNegotiateAuthScheme));
}
TEST_F(NetworkServiceTest, AuthSchemesNone) {
// An empty list means to support no schemes.
service()->SetUpHttpAuth(mojom::HttpAuthStaticParams::New());
mojom::NetworkContextPtr network_context_ptr;
NetworkContext network_context(service(),
mojo::MakeRequest(&network_context_ptr),
CreateContextParams());
net::HttpAuthHandlerRegistryFactory* auth_handler_factory =
reinterpret_cast<net::HttpAuthHandlerRegistryFactory*>(
network_context.url_request_context()->http_auth_handler_factory());
ASSERT_TRUE(auth_handler_factory);
EXPECT_FALSE(auth_handler_factory->GetSchemeFactory(net::kBasicAuthScheme));
EXPECT_FALSE(auth_handler_factory->GetSchemeFactory(net::kDigestAuthScheme));
EXPECT_FALSE(auth_handler_factory->GetSchemeFactory(net::kNtlmAuthScheme));
}
// |allow_gssapi_library_load| is only supported on ChromeOS.
#if defined(OS_CHROMEOS)
TEST_F(NetworkServiceTest, AuthGssapiLibraryDisabled) {
mojom::HttpAuthStaticParamsPtr auth_params =
mojom::HttpAuthStaticParams::New();
auth_params->supported_schemes.push_back("negotiate");
auth_params->allow_gssapi_library_load = true;
service()->SetUpHttpAuth(std::move(auth_params));
mojom::NetworkContextPtr network_context_ptr;
NetworkContext network_context(service(),
mojo::MakeRequest(&network_context_ptr),
CreateContextParams());
ASSERT_TRUE(GetNegotiateFactory(&network_context));
EXPECT_TRUE(GetNegotiateFactory(&network_context)
->allow_gssapi_library_load_for_testing());
}
#endif // defined(OS_CHROMEOS)
// |gssapi_library_name| is only supported on certain POSIX platforms.
#if BUILDFLAG(USE_KERBEROS) && defined(OS_POSIX) && !defined(OS_ANDROID) && \
!defined(OS_CHROMEOS)
TEST_F(NetworkServiceTest, AuthGssapiLibraryName) {
const std::string kGssapiLibraryName = "Jim";
mojom::HttpAuthStaticParamsPtr auth_params =
mojom::HttpAuthStaticParams::New();
auth_params->supported_schemes.push_back("negotiate");
auth_params->gssapi_library_name = kGssapiLibraryName;
service()->SetUpHttpAuth(std::move(auth_params));
mojom::NetworkContextPtr network_context_ptr;
NetworkContext network_context(service(),
mojo::MakeRequest(&network_context_ptr),
CreateContextParams());
ASSERT_TRUE(GetNegotiateFactory(&network_context));
EXPECT_EQ(kGssapiLibraryName,
GetNegotiateFactory(&network_context)->GetLibraryNameForTesting());
}
#endif
TEST_F(NetworkServiceTest, AuthServerWhitelist) {
// Add one server to the whitelist before creating any NetworkContexts.
mojom::HttpAuthDynamicParamsPtr auth_params =
mojom::HttpAuthDynamicParams::New();
auth_params->server_whitelist = "server1";
service()->ConfigureHttpAuthPrefs(std::move(auth_params));
// Create a network context, which should reflect the whitelist.
mojom::NetworkContextPtr network_context_ptr;
NetworkContext network_context(service(),
mojo::MakeRequest(&network_context_ptr),
CreateContextParams());
net::HttpAuthHandlerFactory* auth_handler_factory =
network_context.url_request_context()->http_auth_handler_factory();
ASSERT_TRUE(auth_handler_factory);
ASSERT_TRUE(auth_handler_factory->http_auth_preferences());
EXPECT_TRUE(
auth_handler_factory->http_auth_preferences()->CanUseDefaultCredentials(
GURL("https://server1/")));
EXPECT_FALSE(
auth_handler_factory->http_auth_preferences()->CanUseDefaultCredentials(
GURL("https://server2/")));
// Change whitelist to only have a different server on it. The pre-existing
// NetworkContext should be using the new list.
auth_params = mojom::HttpAuthDynamicParams::New();
auth_params->server_whitelist = "server2";
service()->ConfigureHttpAuthPrefs(std::move(auth_params));
EXPECT_FALSE(
auth_handler_factory->http_auth_preferences()->CanUseDefaultCredentials(
GURL("https://server1/")));
EXPECT_TRUE(
auth_handler_factory->http_auth_preferences()->CanUseDefaultCredentials(
GURL("https://server2/")));
// Change whitelist to have multiple servers. The pre-existing NetworkContext
// should be using the new list.
auth_params = mojom::HttpAuthDynamicParams::New();
auth_params->server_whitelist = "server1,server2";
service()->ConfigureHttpAuthPrefs(std::move(auth_params));
EXPECT_TRUE(
auth_handler_factory->http_auth_preferences()->CanUseDefaultCredentials(
GURL("https://server1/")));
EXPECT_TRUE(
auth_handler_factory->http_auth_preferences()->CanUseDefaultCredentials(
GURL("https://server2/")));
}
TEST_F(NetworkServiceTest, AuthDelegateWhitelist) {
// Add one server to the whitelist before creating any NetworkContexts.
mojom::HttpAuthDynamicParamsPtr auth_params =
mojom::HttpAuthDynamicParams::New();
auth_params->delegate_whitelist = "server1";
service()->ConfigureHttpAuthPrefs(std::move(auth_params));
// Create a network context, which should reflect the whitelist.
mojom::NetworkContextPtr network_context_ptr;
NetworkContext network_context(service(),
mojo::MakeRequest(&network_context_ptr),
CreateContextParams());
net::HttpAuthHandlerFactory* auth_handler_factory =
network_context.url_request_context()->http_auth_handler_factory();
ASSERT_TRUE(auth_handler_factory);
ASSERT_TRUE(auth_handler_factory->http_auth_preferences());
EXPECT_TRUE(auth_handler_factory->http_auth_preferences()->CanDelegate(
GURL("https://server1/")));
EXPECT_FALSE(auth_handler_factory->http_auth_preferences()->CanDelegate(
GURL("https://server2/")));
// Change whitelist to only have a different server on it. The pre-existing
// NetworkContext should be using the new list.
auth_params = mojom::HttpAuthDynamicParams::New();
auth_params->delegate_whitelist = "server2";
service()->ConfigureHttpAuthPrefs(std::move(auth_params));
EXPECT_FALSE(auth_handler_factory->http_auth_preferences()->CanDelegate(
GURL("https://server1/")));
EXPECT_TRUE(auth_handler_factory->http_auth_preferences()->CanDelegate(
GURL("https://server2/")));
// Change whitelist to have multiple servers. The pre-existing NetworkContext
// should be using the new list.
auth_params = mojom::HttpAuthDynamicParams::New();
auth_params->delegate_whitelist = "server1,server2";
service()->ConfigureHttpAuthPrefs(std::move(auth_params));
EXPECT_TRUE(auth_handler_factory->http_auth_preferences()->CanDelegate(
GURL("https://server1/")));
EXPECT_TRUE(auth_handler_factory->http_auth_preferences()->CanDelegate(
GURL("https://server2/")));
}
TEST_F(NetworkServiceTest, AuthNegotiateCnameLookup) {
// Set |negotiate_disable_cname_lookup| to true before creating any
// NetworkContexts.
mojom::HttpAuthDynamicParamsPtr auth_params =
mojom::HttpAuthDynamicParams::New();
auth_params->negotiate_disable_cname_lookup = true;
service()->ConfigureHttpAuthPrefs(std::move(auth_params));
// Create a network context, which should reflect the setting.
mojom::NetworkContextPtr network_context_ptr;
NetworkContext network_context(service(),
mojo::MakeRequest(&network_context_ptr),
CreateContextParams());
net::HttpAuthHandlerFactory* auth_handler_factory =
network_context.url_request_context()->http_auth_handler_factory();
ASSERT_TRUE(auth_handler_factory);
ASSERT_TRUE(auth_handler_factory->http_auth_preferences());
EXPECT_TRUE(auth_handler_factory->http_auth_preferences()
->NegotiateDisableCnameLookup());
// Set it to false. The pre-existing NetworkContext should be using the new
// setting.
auth_params = mojom::HttpAuthDynamicParams::New();
auth_params->negotiate_disable_cname_lookup = false;
service()->ConfigureHttpAuthPrefs(std::move(auth_params));
EXPECT_FALSE(auth_handler_factory->http_auth_preferences()
->NegotiateDisableCnameLookup());
// Set it back to true. The pre-existing NetworkContext should be using the
// new setting.
auth_params = mojom::HttpAuthDynamicParams::New();
auth_params->negotiate_disable_cname_lookup = true;
service()->ConfigureHttpAuthPrefs(std::move(auth_params));
EXPECT_TRUE(auth_handler_factory->http_auth_preferences()
->NegotiateDisableCnameLookup());
}
TEST_F(NetworkServiceTest, AuthEnableNegotiatePort) {
// Set |enable_negotiate_port| to true before creating any NetworkContexts.
mojom::HttpAuthDynamicParamsPtr auth_params =
mojom::HttpAuthDynamicParams::New();
auth_params->enable_negotiate_port = true;
service()->ConfigureHttpAuthPrefs(std::move(auth_params));
// Create a network context, which should reflect the setting.
mojom::NetworkContextPtr network_context_ptr;
NetworkContext network_context(service(),
mojo::MakeRequest(&network_context_ptr),
CreateContextParams());
net::HttpAuthHandlerFactory* auth_handler_factory =
network_context.url_request_context()->http_auth_handler_factory();
ASSERT_TRUE(auth_handler_factory);
ASSERT_TRUE(auth_handler_factory->http_auth_preferences());
EXPECT_TRUE(
auth_handler_factory->http_auth_preferences()->NegotiateEnablePort());
// Set it to false. The pre-existing NetworkContext should be using the new
// setting.
auth_params = mojom::HttpAuthDynamicParams::New();
auth_params->enable_negotiate_port = false;
service()->ConfigureHttpAuthPrefs(std::move(auth_params));
EXPECT_FALSE(
auth_handler_factory->http_auth_preferences()->NegotiateEnablePort());
// Set it back to true. The pre-existing NetworkContext should be using the
// new setting.
auth_params = mojom::HttpAuthDynamicParams::New();
auth_params->enable_negotiate_port = true;
service()->ConfigureHttpAuthPrefs(std::move(auth_params));
EXPECT_TRUE(
auth_handler_factory->http_auth_preferences()->NegotiateEnablePort());
}
// DnsClient isn't supported on iOS.
#if !defined(OS_IOS)
TEST_F(NetworkServiceTest, DnsClientEnableDisable) {
// HostResolver::GetDnsConfigAsValue() returns nullptr if the stub resolver is
// disabled.
EXPECT_FALSE(service()->host_resolver()->GetDnsConfigAsValue());
service()->ConfigureStubHostResolver(
true /* stub_resolver_enabled */,
base::nullopt /* dns_over_https_servers */);
EXPECT_TRUE(service()->host_resolver()->GetDnsConfigAsValue());
service()->ConfigureStubHostResolver(
false /* stub_resolver_enabled */,
base::nullopt /* dns_over_https_servers */);
EXPECT_FALSE(service()->host_resolver()->GetDnsConfigAsValue());
}
TEST_F(NetworkServiceTest, DnsOverHttpsEnableDisable) {
const GURL kServer1 = GURL("https://foo/");
const bool kServer1UsePost = false;
const GURL kServer2 = GURL("https://bar/");
const bool kServer2UsePost = true;
const GURL kServer3 = GURL("https://grapefruit/");
const bool kServer3UsePost = false;
// HostResolver::GetDnsClientForTesting() returns nullptr if the stub resolver
// is disabled.
EXPECT_FALSE(service()->host_resolver()->GetDnsConfigAsValue());
// Create the primary NetworkContext before enabling DNS over HTTPS.
mojom::NetworkContextPtr network_context;
mojom::NetworkContextParamsPtr context_params = CreateContextParams();
context_params->primary_network_context = true;
service()->CreateNetworkContext(mojo::MakeRequest(&network_context),
std::move(context_params));
// Enable DNS over HTTPS for one server.
std::vector<mojom::DnsOverHttpsServerPtr> dns_over_https_servers_ptr;
mojom::DnsOverHttpsServerPtr dns_over_https_server =
mojom::DnsOverHttpsServer::New();
dns_over_https_server->url = kServer1;
dns_over_https_server->use_posts = kServer1UsePost;
dns_over_https_servers_ptr.emplace_back(std::move(dns_over_https_server));
service()->ConfigureStubHostResolver(true /* stub_resolver_enabled */,
std::move(dns_over_https_servers_ptr));
EXPECT_TRUE(service()->host_resolver()->GetDnsConfigAsValue());
const auto* dns_over_https_servers =
service()->host_resolver()->GetDnsOverHttpsServersForTesting();
ASSERT_TRUE(dns_over_https_servers);
ASSERT_EQ(1u, dns_over_https_servers->size());
EXPECT_EQ(kServer1, (*dns_over_https_servers)[0].server);
EXPECT_EQ(kServer1UsePost, (*dns_over_https_servers)[0].use_post);
// Enable DNS over HTTPS for two servers.
dns_over_https_servers_ptr.clear();
dns_over_https_server = mojom::DnsOverHttpsServer::New();
dns_over_https_server->url = kServer2;
dns_over_https_server->use_posts = kServer2UsePost;
dns_over_https_servers_ptr.emplace_back(std::move(dns_over_https_server));
dns_over_https_server = mojom::DnsOverHttpsServer::New();
dns_over_https_server->url = kServer3;
dns_over_https_server->use_posts = kServer3UsePost;
dns_over_https_servers_ptr.emplace_back(std::move(dns_over_https_server));
service()->ConfigureStubHostResolver(true /* stub_resolver_enabled */,
std::move(dns_over_https_servers_ptr));
EXPECT_TRUE(service()->host_resolver()->GetDnsConfigAsValue());
dns_over_https_servers =
service()->host_resolver()->GetDnsOverHttpsServersForTesting();
ASSERT_TRUE(dns_over_https_servers);
ASSERT_EQ(2u, dns_over_https_servers->size());
EXPECT_EQ(kServer2, (*dns_over_https_servers)[0].server);
EXPECT_EQ(kServer2UsePost, (*dns_over_https_servers)[0].use_post);
EXPECT_EQ(kServer3, (*dns_over_https_servers)[1].server);
EXPECT_EQ(kServer3UsePost, (*dns_over_https_servers)[1].use_post);
// Destroying the primary NetworkContext should disable DNS over HTTPS.
network_context.reset();
base::RunLoop().RunUntilIdle();
// DnsClient is still enabled.
EXPECT_TRUE(service()->host_resolver()->GetDnsConfigAsValue());
// DNS over HTTPS is not.
EXPECT_FALSE(service()->host_resolver()->GetDnsOverHttpsServersForTesting());
}
// Make sure that enabling DNS over HTTP without a primary NetworkContext fails.
TEST_F(NetworkServiceTest,
DnsOverHttpsEnableDoesNothingWithoutPrimaryNetworkContext) {
// HostResolver::GetDnsClientForTesting() returns nullptr if the stub resolver
// is disabled.
EXPECT_FALSE(service()->host_resolver()->GetDnsConfigAsValue());
// Try to enable DnsClient and DNS over HTTPS. Only the first should take
// effect.
std::vector<mojom::DnsOverHttpsServerPtr> dns_over_https_servers;
mojom::DnsOverHttpsServerPtr dns_over_https_server =
mojom::DnsOverHttpsServer::New();
dns_over_https_server->url = GURL("https://foo/");
dns_over_https_servers.emplace_back(std::move(dns_over_https_server));
service()->ConfigureStubHostResolver(true /* stub_resolver_enabled */,
std::move(dns_over_https_servers));
// DnsClient is enabled.
EXPECT_TRUE(service()->host_resolver()->GetDnsConfigAsValue());
// DNS over HTTPS is not.
EXPECT_FALSE(service()->host_resolver()->GetDnsOverHttpsServersForTesting());
// Create a NetworkContext that is not the primary one.
mojom::NetworkContextPtr network_context;
service()->CreateNetworkContext(mojo::MakeRequest(&network_context),
CreateContextParams());
// There should be no change in host resolver state.
EXPECT_TRUE(service()->host_resolver()->GetDnsConfigAsValue());
EXPECT_FALSE(service()->host_resolver()->GetDnsOverHttpsServersForTesting());
// Try to enable DNS over HTTPS again, which should not work, since there's
// still no primary NetworkContext.
dns_over_https_servers.clear();
dns_over_https_server = mojom::DnsOverHttpsServer::New();
dns_over_https_server->url = GURL("https://foo2/");
dns_over_https_servers.emplace_back(std::move(dns_over_https_server));
service()->ConfigureStubHostResolver(true /* stub_resolver_enabled */,
std::move(dns_over_https_servers));
// There should be no change in host resolver state.
EXPECT_TRUE(service()->host_resolver()->GetDnsConfigAsValue());
EXPECT_FALSE(service()->host_resolver()->GetDnsOverHttpsServersForTesting());
}
#endif // !defined(OS_IOS)
// |ntlm_v2_enabled| is only supported on POSIX platforms.
#if defined(OS_POSIX)
TEST_F(NetworkServiceTest, AuthNtlmV2Enabled) {
// Set |ntlm_v2_enabled| to false before creating any NetworkContexts.
mojom::HttpAuthDynamicParamsPtr auth_params =
mojom::HttpAuthDynamicParams::New();
auth_params->ntlm_v2_enabled = false;
service()->ConfigureHttpAuthPrefs(std::move(auth_params));
// Create a network context, which should reflect the setting.
mojom::NetworkContextPtr network_context_ptr;
NetworkContext network_context(service(),
mojo::MakeRequest(&network_context_ptr),
CreateContextParams());
net::HttpAuthHandlerFactory* auth_handler_factory =
network_context.url_request_context()->http_auth_handler_factory();
ASSERT_TRUE(auth_handler_factory);
ASSERT_TRUE(auth_handler_factory->http_auth_preferences());
EXPECT_FALSE(auth_handler_factory->http_auth_preferences()->NtlmV2Enabled());
// Set it to true. The pre-existing NetworkContext should be using the new
// setting.
auth_params = mojom::HttpAuthDynamicParams::New();
auth_params->ntlm_v2_enabled = true;
service()->ConfigureHttpAuthPrefs(std::move(auth_params));
EXPECT_TRUE(auth_handler_factory->http_auth_preferences()->NtlmV2Enabled());
// Set it back to false. The pre-existing NetworkContext should be using the
// new setting.
auth_params = mojom::HttpAuthDynamicParams::New();
auth_params->ntlm_v2_enabled = false;
service()->ConfigureHttpAuthPrefs(std::move(auth_params));
EXPECT_FALSE(auth_handler_factory->http_auth_preferences()->NtlmV2Enabled());
}
#endif // defined(OS_POSIX)
// |android_negotiate_account_type| is only supported on Android.
#if defined(OS_ANDROID)
TEST_F(NetworkServiceTest, AuthAndroidNegotiateAccountType) {
const char kInitialAccountType[] = "Scorpio";
const char kFinalAccountType[] = "Pisces";
// Set |android_negotiate_account_type| to before creating any
// NetworkContexts.
mojom::HttpAuthDynamicParamsPtr auth_params =
mojom::HttpAuthDynamicParams::New();
auth_params->android_negotiate_account_type = kInitialAccountType;
service()->ConfigureHttpAuthPrefs(std::move(auth_params));
// Create a network context, which should reflect the setting.
mojom::NetworkContextPtr network_context_ptr;
NetworkContext network_context(service(),
mojo::MakeRequest(&network_context_ptr),
CreateContextParams());
net::HttpAuthHandlerFactory* auth_handler_factory =
network_context.url_request_context()->http_auth_handler_factory();
ASSERT_TRUE(auth_handler_factory);
ASSERT_TRUE(auth_handler_factory->http_auth_preferences());
EXPECT_EQ(kInitialAccountType, auth_handler_factory->http_auth_preferences()
->AuthAndroidNegotiateAccountType());
// Change |android_negotiate_account_type|. The pre-existing NetworkContext
// should be using the new setting.
auth_params = mojom::HttpAuthDynamicParams::New();
auth_params->android_negotiate_account_type = kFinalAccountType;
service()->ConfigureHttpAuthPrefs(std::move(auth_params));
EXPECT_EQ(kFinalAccountType, auth_handler_factory->http_auth_preferences()
->AuthAndroidNegotiateAccountType());
}
#endif // defined(OS_ANDROID)
class ServiceTestClient : public service_manager::test::ServiceTestClient,
public service_manager::mojom::ServiceFactory {
public:
explicit ServiceTestClient(service_manager::test::ServiceTest* test)
: service_manager::test::ServiceTestClient(test) {
registry_.AddInterface<service_manager::mojom::ServiceFactory>(base::Bind(
&ServiceTestClient::BindServiceFactoryRequest, base::Unretained(this)));
}
~ServiceTestClient() override {}
protected:
void OnBindInterface(const service_manager::BindSourceInfo& source_info,
const std::string& interface_name,
mojo::ScopedMessagePipeHandle interface_pipe) override {
registry_.BindInterface(interface_name, std::move(interface_pipe));
}
void CreateService(
service_manager::mojom::ServiceRequest request,
const std::string& name,
service_manager::mojom::PIDReceiverPtr pid_receiver) override {
if (name == kNetworkServiceName) {
service_context_.reset(new service_manager::ServiceContext(
NetworkService::CreateForTesting(), std::move(request)));
}
}
void BindServiceFactoryRequest(
service_manager::mojom::ServiceFactoryRequest request) {
service_factory_bindings_.AddBinding(this, std::move(request));
}
std::unique_ptr<service_manager::ServiceContext> service_context_;
private:
service_manager::BinderRegistry registry_;
mojo::BindingSet<service_manager::mojom::ServiceFactory>
service_factory_bindings_;
};
class NetworkServiceTestWithService
: public service_manager::test::ServiceTest {
public:
NetworkServiceTestWithService()
: ServiceTest("network_unittests",
base::test::ScopedTaskEnvironment::MainThreadType::IO) {}
~NetworkServiceTestWithService() override {}
void CreateNetworkContext() {
mojom::NetworkContextParamsPtr context_params =
mojom::NetworkContextParams::New();
network_service_->CreateNetworkContext(mojo::MakeRequest(&network_context_),
std::move(context_params));
}
void LoadURL(const GURL& url, int options = mojom::kURLLoadOptionNone) {
ResourceRequest request;
request.url = url;
request.method = "GET";
request.request_initiator = url::Origin();
StartLoadingURL(request, 0 /* process_id */, options);
client_->RunUntilComplete();
}
void StartLoadingURL(const ResourceRequest& request,
uint32_t process_id,
int options = mojom::kURLLoadOptionNone) {
client_.reset(new TestURLLoaderClient());
mojom::URLLoaderFactoryPtr loader_factory;
mojom::URLLoaderFactoryParamsPtr params =
mojom::URLLoaderFactoryParams::New();
params->process_id = process_id;
params->is_corb_enabled = false;
network_context_->CreateURLLoaderFactory(mojo::MakeRequest(&loader_factory),
std::move(params));
loader_factory->CreateLoaderAndStart(
mojo::MakeRequest(&loader_), 1, 1, options, request,
client_->CreateInterfacePtr(),
net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS));
}
net::EmbeddedTestServer* test_server() { return &test_server_; }
TestURLLoaderClient* client() { return client_.get(); }
mojom::URLLoader* loader() { return loader_.get(); }
mojom::NetworkService* service() { return network_service_.get(); }
mojom::NetworkContext* context() { return network_context_.get(); }
protected:
std::unique_ptr<service_manager::Service> CreateService() override {
return std::make_unique<ServiceTestClient>(this);
}
void SetUp() override {
test_server_.AddDefaultHandlers(base::FilePath(kServicesTestData));
ASSERT_TRUE(test_server_.Start());
service_manager::test::ServiceTest::SetUp();
connector()->BindInterface(kNetworkServiceName, &network_service_);
}
net::EmbeddedTestServer test_server_;
std::unique_ptr<TestURLLoaderClient> client_;
mojom::NetworkServicePtr network_service_;
mojom::NetworkContextPtr network_context_;
mojom::URLLoaderPtr loader_;
DISALLOW_COPY_AND_ASSIGN(NetworkServiceTestWithService);
};
// Verifies that loading a URL through the network service's mojo interface
// works.
TEST_F(NetworkServiceTestWithService, Basic) {
CreateNetworkContext();
LoadURL(test_server()->GetURL("/echo"));
EXPECT_EQ(net::OK, client()->completion_status().error_code);
}
// Verifies that raw headers are only reported if requested.
TEST_F(NetworkServiceTestWithService, RawRequestHeadersAbsent) {
CreateNetworkContext();
ResourceRequest request;
request.url = test_server()->GetURL("/server-redirect?/echo");
request.method = "GET";
request.request_initiator = url::Origin();
StartLoadingURL(request, 0);
client()->RunUntilRedirectReceived();
EXPECT_TRUE(client()->has_received_redirect());
EXPECT_TRUE(!client()->response_head().raw_request_response_info);
loader()->FollowRedirect(base::nullopt, base::nullopt);
client()->RunUntilComplete();
EXPECT_TRUE(!client()->response_head().raw_request_response_info);
}
TEST_F(NetworkServiceTestWithService, RawRequestHeadersPresent) {
CreateNetworkContext();
ResourceRequest request;
request.url = test_server()->GetURL("/server-redirect?/echo");
request.method = "GET";
request.report_raw_headers = true;
request.request_initiator = url::Origin();
StartLoadingURL(request, 0);
client()->RunUntilRedirectReceived();
EXPECT_TRUE(client()->has_received_redirect());
{
scoped_refptr<HttpRawRequestResponseInfo> request_response_info =
client()->response_head().raw_request_response_info;
ASSERT_TRUE(request_response_info);
EXPECT_EQ(301, request_response_info->http_status_code);
EXPECT_EQ("Moved Permanently", request_response_info->http_status_text);
EXPECT_TRUE(base::StartsWith(request_response_info->request_headers_text,
"GET /server-redirect?/echo HTTP/1.1\r\n",
base::CompareCase::SENSITIVE));
EXPECT_GE(request_response_info->request_headers.size(), 1lu);
EXPECT_GE(request_response_info->response_headers.size(), 1lu);
EXPECT_TRUE(base::StartsWith(request_response_info->response_headers_text,
"HTTP/1.1 301 Moved Permanently\r",
base::CompareCase::SENSITIVE));
}
loader()->FollowRedirect(base::nullopt, base::nullopt);
client()->RunUntilComplete();
{
scoped_refptr<HttpRawRequestResponseInfo> request_response_info =
client()->response_head().raw_request_response_info;
EXPECT_EQ(200, request_response_info->http_status_code);
EXPECT_EQ("OK", request_response_info->http_status_text);
EXPECT_TRUE(base::StartsWith(request_response_info->request_headers_text,
"GET /echo HTTP/1.1\r\n",
base::CompareCase::SENSITIVE));
EXPECT_GE(request_response_info->request_headers.size(), 1lu);
EXPECT_GE(request_response_info->response_headers.size(), 1lu);
EXPECT_TRUE(base::StartsWith(request_response_info->response_headers_text,
"HTTP/1.1 200 OK\r",
base::CompareCase::SENSITIVE));
}
}
TEST_F(NetworkServiceTestWithService, RawRequestAccessControl) {
const uint32_t process_id = 42;
CreateNetworkContext();
ResourceRequest request;
request.url = test_server()->GetURL("/nocache.html");
request.method = "GET";
request.report_raw_headers = true;
request.request_initiator = url::Origin();
StartLoadingURL(request, process_id);
client()->RunUntilComplete();
EXPECT_FALSE(client()->response_head().raw_request_response_info);
service()->SetRawHeadersAccess(process_id, true);
StartLoadingURL(request, process_id);
client()->RunUntilComplete();
{
scoped_refptr<HttpRawRequestResponseInfo> request_response_info =
client()->response_head().raw_request_response_info;
ASSERT_TRUE(request_response_info);
EXPECT_EQ(200, request_response_info->http_status_code);
EXPECT_EQ("OK", request_response_info->http_status_text);
}
service()->SetRawHeadersAccess(process_id, false);
StartLoadingURL(request, process_id);
client()->RunUntilComplete();
EXPECT_FALSE(client()->response_head().raw_request_response_info.get());
}
TEST_F(NetworkServiceTestWithService, SetNetworkConditions) {
const base::UnguessableToken profile_id = base::UnguessableToken::Create();
CreateNetworkContext();
mojom::NetworkConditionsPtr network_conditions =
mojom::NetworkConditions::New();
network_conditions->offline = true;
context()->SetNetworkConditions(profile_id, std::move(network_conditions));
ResourceRequest request;
request.url = test_server()->GetURL("/nocache.html");
request.method = "GET";
StartLoadingURL(request, 0);
client()->RunUntilComplete();
EXPECT_EQ(net::OK, client()->completion_status().error_code);
request.throttling_profile_id = profile_id;
StartLoadingURL(request, 0);
client()->RunUntilComplete();
EXPECT_EQ(net::ERR_INTERNET_DISCONNECTED,
client()->completion_status().error_code);
network_conditions = mojom::NetworkConditions::New();
network_conditions->offline = false;
context()->SetNetworkConditions(profile_id, std::move(network_conditions));
StartLoadingURL(request, 0);
client()->RunUntilComplete();
EXPECT_EQ(net::OK, client()->completion_status().error_code);
network_conditions = mojom::NetworkConditions::New();
network_conditions->offline = true;
context()->SetNetworkConditions(profile_id, std::move(network_conditions));
request.throttling_profile_id = profile_id;
StartLoadingURL(request, 0);
client()->RunUntilComplete();
EXPECT_EQ(net::ERR_INTERNET_DISCONNECTED,
client()->completion_status().error_code);
context()->SetNetworkConditions(profile_id, nullptr);
StartLoadingURL(request, 0);
client()->RunUntilComplete();
EXPECT_EQ(net::OK, client()->completion_status().error_code);
}
// The SpawnedTestServer does not work on iOS.
#if !defined(OS_IOS)
class AllowBadCertsNetworkServiceClient : public mojom::NetworkServiceClient {
public:
explicit AllowBadCertsNetworkServiceClient(
mojom::NetworkServiceClientRequest network_service_client_request)
: binding_(this, std::move(network_service_client_request)) {}
~AllowBadCertsNetworkServiceClient() override {}
// mojom::NetworkServiceClient implementation:
void OnAuthRequired(
uint32_t process_id,
uint32_t routing_id,
uint32_t request_id,
const GURL& url,
const GURL& site_for_cookies,
bool first_auth_attempt,
const scoped_refptr<net::AuthChallengeInfo>& auth_info,
int32_t resource_type,
const base::Optional<ResourceResponseHead>& head,
mojom::AuthChallengeResponderPtr auth_challenge_responder) override {
NOTREACHED();
}
void OnCertificateRequested(
uint32_t process_id,
uint32_t routing_id,
uint32_t request_id,
const scoped_refptr<net::SSLCertRequestInfo>& cert_info,
mojom::NetworkServiceClient::OnCertificateRequestedCallback callback)
override {
NOTREACHED();
}
void OnSSLCertificateError(uint32_t process_id,
uint32_t routing_id,
uint32_t request_id,
int32_t resource_type,
const GURL& url,
const net::SSLInfo& ssl_info,
bool fatal,
OnSSLCertificateErrorCallback response) override {
std::move(response).Run(net::OK);
}
void OnFileUploadRequested(uint32_t process_id,
bool async,
const std::vector<base::FilePath>& file_paths,
OnFileUploadRequestedCallback callback) override {
NOTREACHED();
}
void OnCookiesRead(int process_id,
int routing_id,
const GURL& url,
const GURL& first_party_url,
const net::CookieList& cookie_list,
bool blocked_by_policy) override {
NOTREACHED();
}
void OnCookieChange(int process_id,
int routing_id,
const GURL& url,
const GURL& first_party_url,
const net::CanonicalCookie& cookie,
bool blocked_by_policy) override {
NOTREACHED();
}
private:
mojo::Binding<mojom::NetworkServiceClient> binding_;
DISALLOW_COPY_AND_ASSIGN(AllowBadCertsNetworkServiceClient);
};
// Test |primary_network_context|, which is required by AIA fetching, among
// other things.
TEST_F(NetworkServiceTestWithService, AIAFetching) {
mojom::NetworkContextParamsPtr context_params = CreateContextParams();
mojom::NetworkServiceClientPtr network_service_client;
context_params->primary_network_context = true;
// Have to allow bad certs when using
// SpawnedTestServer::SSLOptions::CERT_AUTO_AIA_INTERMEDIATE.
AllowBadCertsNetworkServiceClient allow_bad_certs_client(
mojo::MakeRequest(&network_service_client));
network_service_->CreateNetworkContext(mojo::MakeRequest(&network_context_),
std::move(context_params));
net::SpawnedTestServer::SSLOptions ssl_options(
net::SpawnedTestServer::SSLOptions::CERT_AUTO_AIA_INTERMEDIATE);
net::SpawnedTestServer test_server(net::SpawnedTestServer::TYPE_HTTPS,
ssl_options,
base::FilePath(kServicesTestData));
ASSERT_TRUE(test_server.Start());
LoadURL(test_server.GetURL("/echo"),
mojom::kURLLoadOptionSendSSLInfoWithResponse);
EXPECT_EQ(net::OK, client()->completion_status().error_code);
EXPECT_EQ(
0u, client()->response_head().cert_status & net::CERT_STATUS_ALL_ERRORS);
ASSERT_TRUE(client()->ssl_info());
ASSERT_TRUE(client()->ssl_info()->cert);
EXPECT_EQ(2u, client()->ssl_info()->cert->intermediate_buffers().size());
ASSERT_TRUE(client()->ssl_info()->unverified_cert);
EXPECT_EQ(
0u, client()->ssl_info()->unverified_cert->intermediate_buffers().size());
}
#endif // !defined(OS_IOS)
// Check that destroying a NetworkContext with |primary_network_context| set
// destroys all other NetworkContexts.
TEST_F(NetworkServiceTestWithService,
DestroyingPrimaryNetworkContextDestroysOtherContexts) {
mojom::NetworkContextParamsPtr context_params = CreateContextParams();
context_params->primary_network_context = true;
mojom::NetworkContextPtr cert_validating_network_context;
network_service_->CreateNetworkContext(
mojo::MakeRequest(&cert_validating_network_context),
std::move(context_params));
base::RunLoop run_loop;
mojom::NetworkContextPtr network_context;
network_service_->CreateNetworkContext(mojo::MakeRequest(&network_context),
CreateContextParams());
network_context.set_connection_error_handler(run_loop.QuitClosure());
// Destroying |cert_validating_network_context| should result in destroying
// |network_context| as well.
cert_validating_network_context.reset();
run_loop.Run();
EXPECT_TRUE(network_context.encountered_error());
}
class TestNetworkChangeManagerClient
: public mojom::NetworkChangeManagerClient {
public:
explicit TestNetworkChangeManagerClient(
mojom::NetworkService* network_service)
: connection_type_(mojom::ConnectionType::CONNECTION_UNKNOWN),
binding_(this) {
mojom::NetworkChangeManagerPtr manager_ptr;
mojom::NetworkChangeManagerRequest request(mojo::MakeRequest(&manager_ptr));
network_service->GetNetworkChangeManager(std::move(request));
mojom::NetworkChangeManagerClientPtr client_ptr;
mojom::NetworkChangeManagerClientRequest client_request(
mojo::MakeRequest(&client_ptr));
binding_.Bind(std::move(client_request));
manager_ptr->RequestNotifications(std::move(client_ptr));
}
~TestNetworkChangeManagerClient() override {}
// NetworkChangeManagerClient implementation:
void OnInitialConnectionType(mojom::ConnectionType type) override {
if (type == connection_type_)
run_loop_.Quit();
}
void OnNetworkChanged(mojom::ConnectionType type) override {
if (type == connection_type_)
run_loop_.Quit();
}
// Waits for the desired |connection_type| notification.
void WaitForNotification(mojom::ConnectionType type) {
connection_type_ = type;
run_loop_.Run();
}
private:
base::RunLoop run_loop_;
mojom::ConnectionType connection_type_;
mojo::Binding<mojom::NetworkChangeManagerClient> binding_;
DISALLOW_COPY_AND_ASSIGN(TestNetworkChangeManagerClient);
};
class NetworkChangeTest : public testing::Test {
public:
NetworkChangeTest()
: scoped_task_environment_(
base::test::ScopedTaskEnvironment::MainThreadType::IO) {
service_ = NetworkService::CreateForTesting();
}
~NetworkChangeTest() override {}
NetworkService* service() const { return service_.get(); }
private:
base::test::ScopedTaskEnvironment scoped_task_environment_;
#if defined(OS_ANDROID)
// On Android, NetworkChangeNotifier setup is more involved and needs to
// to be split between UI thread and network thread. Use a mock
// NetworkChangeNotifier in tests, so the test setup is simpler.
net::test::MockNetworkChangeNotifier network_change_notifier_;
#endif
std::unique_ptr<NetworkService> service_;
};
// mojom:NetworkChangeManager isn't supported on these platforms.
// See the same ifdef in CreateNetworkChangeNotifierIfNeeded.
#if defined(OS_CHROMEOS) || defined(OS_FUCHSIA) || defined(OS_IOS)
#define MAYBE_NetworkChangeManagerRequest DISABLED_NetworkChangeManagerRequest
#else
#define MAYBE_NetworkChangeManagerRequest NetworkChangeManagerRequest
#endif
TEST_F(NetworkChangeTest, MAYBE_NetworkChangeManagerRequest) {
TestNetworkChangeManagerClient manager_client(service());
net::NetworkChangeNotifier::NotifyObserversOfNetworkChangeForTests(
net::NetworkChangeNotifier::CONNECTION_3G);
manager_client.WaitForNotification(mojom::ConnectionType::CONNECTION_3G);
}
class NetworkServiceNetworkChangeTest
: public service_manager::test::ServiceTest {
public:
NetworkServiceNetworkChangeTest()
: ServiceTest("network_unittests",
base::test::ScopedTaskEnvironment::MainThreadType::IO) {}
~NetworkServiceNetworkChangeTest() override {}
mojom::NetworkService* service() { return network_service_.get(); }
private:
// A ServiceTestClient that broadcasts a network change notification in the
// network service's process.
class ServiceTestClientWithNetworkChange : public ServiceTestClient {
public:
explicit ServiceTestClientWithNetworkChange(
service_manager::test::ServiceTest* test)
: ServiceTestClient(test) {}
~ServiceTestClientWithNetworkChange() override {}
protected:
void CreateService(
service_manager::mojom::ServiceRequest request,
const std::string& name,
service_manager::mojom::PIDReceiverPtr pid_receiver) override {
if (name == kNetworkServiceName) {
service_context_.reset(new service_manager::ServiceContext(
NetworkService::CreateForTesting(), std::move(request)));
// Send a broadcast after NetworkService is actually created.
// Otherwise, this NotifyObservers is a no-op.
net::NetworkChangeNotifier::NotifyObserversOfNetworkChangeForTests(
net::NetworkChangeNotifier::CONNECTION_3G);
}
}
};
std::unique_ptr<service_manager::Service> CreateService() override {
return std::make_unique<ServiceTestClientWithNetworkChange>(this);
}
void SetUp() override {
service_manager::test::ServiceTest::SetUp();
connector()->BindInterface(kNetworkServiceName, &network_service_);
}
mojom::NetworkServicePtr network_service_;
#if defined(OS_ANDROID)
// On Android, NetworkChangeNotifier setup is more involved and needs
// to be split between UI thread and network thread. Use a mock
// NetworkChangeNotifier in tests, so the test setup is simpler.
net::test::MockNetworkChangeNotifier network_change_notifier_;
#endif
DISALLOW_COPY_AND_ASSIGN(NetworkServiceNetworkChangeTest);
};
TEST_F(NetworkServiceNetworkChangeTest, MAYBE_NetworkChangeManagerRequest) {
TestNetworkChangeManagerClient manager_client(service());
manager_client.WaitForNotification(mojom::ConnectionType::CONNECTION_3G);
}
} // namespace
} // namespace network