blob: 9c3bf2e99ee9a6a6c4eb467175bc8840c80dac34 [file] [log] [blame]
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <algorithm>
#include <memory>
#include <optional>
#include <string>
#include <string_view>
#include <vector>
#include "base/functional/callback.h"
#include "base/run_loop.h"
#include "base/test/bind.h"
#include "base/test/scoped_feature_list.h"
#include "base/time/time.h"
#include "net/base/address_family.h"
#include "net/base/features.h"
#include "net/base/net_errors.h"
#include "net/base/network_anonymization_key.h"
#include "net/dns/address_sorter.h"
#include "net/dns/dns_task_results_manager.h"
#include "net/dns/dns_test_util.h"
#include "net/dns/host_resolver.h"
#include "net/dns/host_resolver_manager_service_endpoint_request_impl.h"
#include "net/dns/host_resolver_manager_unittest.h"
#include "net/dns/host_resolver_results_test_util.h"
#include "net/dns/public/host_resolver_results.h"
#include "net/dns/public/host_resolver_source.h"
#include "net/dns/resolve_context.h"
#include "net/log/net_log_with_source.h"
#include "net/test/gtest_util.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/scheme_host_port.h"
using ::testing::ElementsAre;
using ::testing::IsEmpty;
using ::testing::UnorderedElementsAre;
using net::test::IsError;
using net::test::IsOk;
namespace net {
using ServiceEndpointRequest = HostResolver::ServiceEndpointRequest;
using ResolveHostRequest = HostResolver::ResolveHostRequest;
using ResolveHostParameters = HostResolver::ResolveHostParameters;
namespace {
IPEndPoint MakeIPEndPoint(std::string_view ip_literal, uint16_t port = 0) {
std::optional<IPAddress> ip = IPAddress::FromIPLiteral(std::move(ip_literal));
return IPEndPoint(*ip, port);
}
// Sorts endpoints using IPAddress's comparator.
class FakeAddressSorter : public AddressSorter {
public:
void Sort(const std::vector<IPEndPoint>& endpoints,
CallbackType callback) const override {
std::vector<IPEndPoint> sorted = endpoints;
std::sort(sorted.begin(), sorted.end(),
[](const IPEndPoint& a, const IPEndPoint& b) {
return a.address() < b.address();
});
std::move(callback).Run(true, sorted);
}
};
class Requester : public ServiceEndpointRequest::Delegate {
public:
explicit Requester(std::unique_ptr<ServiceEndpointRequest> request)
: request_(std::move(request)) {}
~Requester() override = default;
// ServiceEndpointRequest::Delegate overrides:
void OnServiceEndpointsUpdated() override {
if (on_updated_callback_) {
std::move(on_updated_callback_).Run();
}
}
void OnServiceEndpointRequestFinished(int rv) override {
SetFinishedResult(rv);
if (on_finished_callback_) {
std::move(on_finished_callback_).Run();
}
if (wait_for_finished_callback_) {
std::move(wait_for_finished_callback_).Run();
}
}
int Start() {
int rv = request_->Start(this);
if (rv != ERR_IO_PENDING) {
SetFinishedResult(rv);
}
return rv;
}
void CancelRequest() { request_.reset(); }
void CancelRequestOnUpdated() {
SetOnUpdatedCallback(base::BindLambdaForTesting([&] { CancelRequest(); }));
}
void CancelRequestOnFinished() {
SetOnFinishedCallback(base::BindLambdaForTesting([&] { CancelRequest(); }));
}
void SetOnUpdatedCallback(base::OnceClosure callback) {
CHECK(!finished_result_);
CHECK(!on_updated_callback_);
on_updated_callback_ = std::move(callback);
}
void SetOnFinishedCallback(base::OnceClosure callback) {
CHECK(!finished_result_);
CHECK(!on_finished_callback_);
on_finished_callback_ = std::move(callback);
}
void WaitForFinished() {
CHECK(!finished_result_);
CHECK(!wait_for_finished_callback_);
base::RunLoop run_loop;
wait_for_finished_callback_ = run_loop.QuitClosure();
run_loop.Run();
}
ServiceEndpointRequest* request() const { return request_.get(); }
std::optional<int> finished_result() const { return finished_result_; }
const std::vector<ServiceEndpoint>& finished_endpoints() const {
CHECK(finished_result_.has_value());
return finished_endpoints_;
}
private:
void SetFinishedResult(int rv) {
CHECK(!finished_result_);
finished_result_ = rv;
if (request_) {
finished_endpoints_ = request_->GetEndpointResults();
}
}
std::unique_ptr<ServiceEndpointRequest> request_;
std::optional<int> finished_result_;
std::vector<ServiceEndpoint> finished_endpoints_;
base::OnceClosure wait_for_finished_callback_;
base::OnceClosure on_updated_callback_;
base::OnceClosure on_finished_callback_;
};
class LegacyRequester {
public:
explicit LegacyRequester(std::unique_ptr<ResolveHostRequest> request)
: request_(std::move(request)) {}
~LegacyRequester() = default;
int Start() {
return request_->Start(
base::BindOnce(&LegacyRequester::OnComplete, base::Unretained(this)));
}
void CancelRequest() { request_.reset(); }
std::optional<int> complete_result() const { return complete_result_; }
private:
void OnComplete(int rv) { complete_result_ = rv; }
std::unique_ptr<ResolveHostRequest> request_;
std::optional<int> complete_result_;
};
} // namespace
class HostResolverServiceEndpointRequestTest
: public HostResolverManagerDnsTest {
public:
HostResolverServiceEndpointRequestTest() {
feature_list_.InitAndEnableFeature(features::kUseServiceEndpointRequest);
}
~HostResolverServiceEndpointRequestTest() override = default;
protected:
void SetUp() override {
HostResolverManagerDnsTest::SetUp();
// MockHostResolverProc resolves all requests to "127.0.0.1" when there is
// no rule. Add a rule to prevent the default behavior.
proc_->AddRule(std::string(), ADDRESS_FAMILY_UNSPECIFIED, "192.0.2.1");
}
void SetDnsRules(MockDnsClientRuleList rules) {
CreateResolver();
UseMockDnsClient(CreateValidDnsConfig(), std::move(rules));
}
void UseNoDomanDnsRules(const std::string& host) {
MockDnsClientRuleList rules;
AddDnsRule(&rules, host, dns_protocol::kTypeA,
MockDnsClientRule::ResultType::kNoDomain, /*delay=*/false);
AddDnsRule(&rules, host, dns_protocol::kTypeAAAA,
MockDnsClientRule::ResultType::kNoDomain, /*delay=*/false);
SetDnsRules(std::move(rules));
}
void UseNonDelayedDnsRules(const std::string& host) {
MockDnsClientRuleList rules;
AddDnsRule(&rules, host, dns_protocol::kTypeA,
MockDnsClientRule::ResultType::kOk, /*delay=*/false);
AddDnsRule(&rules, host, dns_protocol::kTypeAAAA,
MockDnsClientRule::ResultType::kOk, /*delay=*/false);
SetDnsRules(std::move(rules));
}
void UseIpv4DelayedDnsRules(const std::string& host) {
MockDnsClientRuleList rules;
AddDnsRule(&rules, host, dns_protocol::kTypeA,
MockDnsClientRule::ResultType::kOk, /*delay=*/true);
AddDnsRule(&rules, host, dns_protocol::kTypeAAAA,
MockDnsClientRule::ResultType::kOk, /*delay=*/false);
SetDnsRules(std::move(rules));
}
void UseIpv6DelayedDnsRules(const std::string& host) {
MockDnsClientRuleList rules;
AddDnsRule(&rules, host, dns_protocol::kTypeA,
MockDnsClientRule::ResultType::kOk, /*delay=*/false);
AddDnsRule(&rules, host, dns_protocol::kTypeAAAA,
MockDnsClientRule::ResultType::kOk, /*delay=*/true);
SetDnsRules(std::move(rules));
}
void UseHttpsDelayedDnsRules(const std::string& host) {
MockDnsClientRuleList rules;
AddDnsRule(&rules, host, dns_protocol::kTypeA,
MockDnsClientRule::ResultType::kOk, /*delay=*/false);
AddDnsRule(&rules, host, dns_protocol::kTypeAAAA,
MockDnsClientRule::ResultType::kOk, /*delay=*/false);
std::vector<DnsResourceRecord> records = {
BuildTestHttpsServiceRecord(host, /*priority=*/1, /*service_name=*/".",
/*params=*/{})};
rules.emplace_back(host, dns_protocol::kTypeHttps,
/*secure=*/false,
MockDnsClientRule::Result(BuildTestDnsResponse(
host, dns_protocol::kTypeHttps, records)),
/*delay=*/true);
SetDnsRules(std::move(rules));
}
std::unique_ptr<ServiceEndpointRequest> CreateRequest(
std::string_view host,
ResolveHostParameters parameters = ResolveHostParameters()) {
return resolver_->CreateServiceEndpointRequest(
url::SchemeHostPort(GURL(host)), NetworkAnonymizationKey(),
NetLogWithSource(), std::move(parameters), resolve_context_.get());
}
Requester CreateRequester(
std::string_view host,
ResolveHostParameters parameters = ResolveHostParameters()) {
return Requester(CreateRequest(host, std::move(parameters)));
}
LegacyRequester CreateLegacyRequester(std::string_view host) {
return LegacyRequester(resolver_->CreateRequest(
url::SchemeHostPort(GURL(host)), NetworkAnonymizationKey(),
NetLogWithSource(), ResolveHostParameters(), resolve_context_.get()));
}
private:
base::test::ScopedFeatureList feature_list_;
};
TEST_F(HostResolverServiceEndpointRequestTest, NameNotResolved) {
UseNoDomanDnsRules("nodomain");
proc_->SignalMultiple(1u);
Requester requester = CreateRequester("https://nodomain");
int rv = requester.Start();
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
requester.WaitForFinished();
EXPECT_THAT(*requester.finished_result(), IsError(ERR_NAME_NOT_RESOLVED));
}
TEST_F(HostResolverServiceEndpointRequestTest, Ok) {
UseNonDelayedDnsRules("ok");
Requester requester = CreateRequester("https://ok");
int rv = requester.Start();
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
requester.WaitForFinished();
EXPECT_THAT(*requester.finished_result(), IsOk());
EXPECT_THAT(requester.finished_endpoints(),
ElementsAre(ExpectServiceEndpoint(
ElementsAre(MakeIPEndPoint("127.0.0.1", 443)),
ElementsAre(MakeIPEndPoint("::1", 443)))));
}
TEST_F(HostResolverServiceEndpointRequestTest, ResolveLocally) {
UseNonDelayedDnsRules("ok");
// The first local only request should complete synchronously with a cache
// miss.
{
ResolveHostParameters parameters;
parameters.source = HostResolverSource::LOCAL_ONLY;
Requester requester = CreateRequester("https://ok", std::move(parameters));
int rv = requester.Start();
EXPECT_THAT(rv, IsError(ERR_DNS_CACHE_MISS));
}
// Populate the cache.
{
Requester requester = CreateRequester("https://ok");
int rv = requester.Start();
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
requester.WaitForFinished();
EXPECT_THAT(*requester.finished_result(), IsOk());
EXPECT_THAT(requester.finished_endpoints(),
ElementsAre(ExpectServiceEndpoint(
ElementsAre(MakeIPEndPoint("127.0.0.1", 443)),
ElementsAre(MakeIPEndPoint("::1", 443)))));
}
// The second local only request should complete synchronously with a cache
// hit.
{
ResolveHostParameters parameters;
parameters.source = HostResolverSource::LOCAL_ONLY;
Requester requester = CreateRequester("https://ok", std::move(parameters));
int rv = requester.Start();
EXPECT_THAT(rv, IsOk());
EXPECT_THAT(requester.finished_endpoints(),
ElementsAre(ExpectServiceEndpoint(
ElementsAre(MakeIPEndPoint("127.0.0.1", 443)),
ElementsAre(MakeIPEndPoint("::1", 443)))));
}
}
TEST_F(HostResolverServiceEndpointRequestTest, EndpointsAreSorted) {
MockDnsClientRuleList rules;
constexpr const char* kHost = "multiple";
DnsResponse a_response = BuildTestDnsResponse(
kHost, dns_protocol::kTypeA,
{BuildTestAddressRecord(kHost, *IPAddress::FromIPLiteral("192.0.2.2")),
BuildTestAddressRecord(kHost, *IPAddress::FromIPLiteral("192.0.2.1"))});
DnsResponse aaaa_response = BuildTestDnsResponse(
kHost, dns_protocol::kTypeAAAA,
{BuildTestAddressRecord(kHost, *IPAddress::FromIPLiteral("2001:db8::2")),
BuildTestAddressRecord(kHost,
*IPAddress::FromIPLiteral("2001:db8::1"))});
AddDnsRule(&rules, kHost, dns_protocol::kTypeA, std::move(a_response),
/*delay=*/false);
AddDnsRule(&rules, kHost, dns_protocol::kTypeAAAA, std::move(aaaa_response),
/*delay=*/false);
CreateResolver();
UseMockDnsClient(CreateValidDnsConfig(), std::move(rules));
mock_dns_client_->SetAddressSorterForTesting(
std::make_unique<FakeAddressSorter>());
Requester requester = CreateRequester("https://multiple");
int rv = requester.Start();
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
requester.WaitForFinished();
EXPECT_THAT(*requester.finished_result(), IsOk());
EXPECT_THAT(requester.finished_endpoints(),
ElementsAre(ExpectServiceEndpoint(
ElementsAre(MakeIPEndPoint("192.0.2.1", 443),
MakeIPEndPoint("192.0.2.2", 443)),
ElementsAre(MakeIPEndPoint("2001:db8::1", 443),
MakeIPEndPoint("2001:db8::2", 443)))));
}
TEST_F(HostResolverServiceEndpointRequestTest, CancelRequestOnUpdated) {
UseIpv4DelayedDnsRules("4slow_ok");
Requester requester = CreateRequester("https://4slow_ok");
requester.CancelRequestOnUpdated();
int rv = requester.Start();
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
RunUntilIdle();
// The finished callback should not be called because the request was
// already cancelled.
ASSERT_FALSE(requester.finished_result().has_value());
ASSERT_FALSE(requester.request());
}
TEST_F(HostResolverServiceEndpointRequestTest, CancelRequestOnFinished) {
UseIpv4DelayedDnsRules("4slow_ok");
Requester requester = CreateRequester("https://4slow_ok");
requester.CancelRequestOnFinished();
int rv = requester.Start();
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
mock_dns_client_->CompleteDelayedTransactions();
requester.WaitForFinished();
// The result should be OK because we cancel the request after completing the
// associated Job.
EXPECT_THAT(*requester.finished_result(), IsOk());
}
TEST_F(HostResolverServiceEndpointRequestTest, Ipv4Slow) {
UseIpv4DelayedDnsRules("4slow_ok");
Requester requester = CreateRequester("https://4slow_ok");
int rv = requester.Start();
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
EXPECT_EQ(3u, resolver_->num_running_dispatcher_jobs_for_tests());
// AAAA and HTTPS should complete.
RunUntilIdle();
EXPECT_EQ(1u, resolver_->num_running_dispatcher_jobs_for_tests());
ASSERT_FALSE(requester.finished_result().has_value());
ASSERT_TRUE(requester.request()->EndpointsCryptoReady());
EXPECT_THAT(requester.request()->GetEndpointResults(),
ElementsAre(ExpectServiceEndpoint(
IsEmpty(), ElementsAre(MakeIPEndPoint("::1", 443)))));
EXPECT_THAT(requester.request()->GetDnsAliasResults(),
UnorderedElementsAre("4slow_ok"));
// Complete A request, which finishes the request synchronously.
mock_dns_client_->CompleteDelayedTransactions();
ASSERT_TRUE(requester.request()->EndpointsCryptoReady());
EXPECT_THAT(*requester.finished_result(), IsOk());
EXPECT_THAT(requester.finished_endpoints(),
ElementsAre(ExpectServiceEndpoint(
ElementsAre(MakeIPEndPoint("127.0.0.1", 443)),
ElementsAre(MakeIPEndPoint("::1", 443)))));
EXPECT_THAT(requester.request()->GetDnsAliasResults(),
UnorderedElementsAre("4slow_ok"));
}
TEST_F(HostResolverServiceEndpointRequestTest, Ipv6Slow) {
UseIpv6DelayedDnsRules("6slow_ok");
Requester requester = CreateRequester("https://6slow_ok");
int rv = requester.Start();
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
EXPECT_EQ(3u, resolver_->num_running_dispatcher_jobs_for_tests());
// A and HTTPS should complete, but no endpoints should be available since
// waiting for AAAA response.
RunUntilIdle();
EXPECT_EQ(1u, resolver_->num_running_dispatcher_jobs_for_tests());
ASSERT_FALSE(requester.finished_result().has_value());
ASSERT_TRUE(requester.request()->EndpointsCryptoReady());
EXPECT_THAT(requester.request()->GetEndpointResults(), IsEmpty());
// Complete AAAA request, which finishes the request synchronously.
mock_dns_client_->CompleteDelayedTransactions();
EXPECT_THAT(*requester.finished_result(), IsOk());
EXPECT_THAT(requester.finished_endpoints(),
ElementsAre(ExpectServiceEndpoint(
ElementsAre(MakeIPEndPoint("127.0.0.1", 443)),
ElementsAre(MakeIPEndPoint("::1", 443)))));
}
TEST_F(HostResolverServiceEndpointRequestTest, Ipv6SlowResolutionDelayPassed) {
UseIpv6DelayedDnsRules("6slow_ok");
Requester requester = CreateRequester("https://6slow_ok");
int rv = requester.Start();
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
EXPECT_EQ(3u, resolver_->num_running_dispatcher_jobs_for_tests());
// A and HTTPS should complete, but no endpoints should be available since
// waiting for AAAA response.
RunUntilIdle();
EXPECT_EQ(1u, resolver_->num_running_dispatcher_jobs_for_tests());
ASSERT_FALSE(requester.finished_result().has_value());
ASSERT_TRUE(requester.request()->EndpointsCryptoReady());
EXPECT_THAT(requester.request()->GetEndpointResults(), IsEmpty());
// The resolution delay timer fired, IPv4 endpoints should be available.
FastForwardBy(DnsTaskResultsManager::kResolutionDelay +
base::Milliseconds(1));
RunUntilIdle();
EXPECT_EQ(1u, resolver_->num_running_dispatcher_jobs_for_tests());
ASSERT_FALSE(requester.finished_result().has_value());
EXPECT_THAT(requester.request()->GetEndpointResults(),
ElementsAre(ExpectServiceEndpoint(
ElementsAre(MakeIPEndPoint("127.0.0.1", 443)), IsEmpty())));
// Complete AAAA request, which finishes the request synchronously.
mock_dns_client_->CompleteDelayedTransactions();
EXPECT_THAT(*requester.finished_result(), IsOk());
EXPECT_THAT(requester.finished_endpoints(),
ElementsAre(ExpectServiceEndpoint(
ElementsAre(MakeIPEndPoint("127.0.0.1", 443)),
ElementsAre(MakeIPEndPoint("::1", 443)))));
}
TEST_F(HostResolverServiceEndpointRequestTest, HttpsSlow) {
UseHttpsDelayedDnsRules("https_slow_ok");
Requester requester = CreateRequester("https://https_slow_ok");
int rv = requester.Start();
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
EXPECT_EQ(3u, resolver_->num_running_dispatcher_jobs_for_tests());
// A and AAAA should complete.
RunUntilIdle();
EXPECT_EQ(1u, resolver_->num_running_dispatcher_jobs_for_tests());
ASSERT_FALSE(requester.finished_result().has_value());
ASSERT_FALSE(requester.request()->EndpointsCryptoReady());
EXPECT_THAT(requester.request()->GetEndpointResults(),
ElementsAre(ExpectServiceEndpoint(
ElementsAre(MakeIPEndPoint("127.0.0.1", 443)),
ElementsAre(MakeIPEndPoint("::1", 443)))));
// Complete HTTPS request, which finishes the request synchronously.
mock_dns_client_->CompleteDelayedTransactions();
EXPECT_THAT(*requester.finished_result(), IsOk());
EXPECT_THAT(
requester.finished_endpoints(),
ElementsAre(
ExpectServiceEndpoint(
ElementsAre(MakeIPEndPoint("127.0.0.1", 443)),
ElementsAre(MakeIPEndPoint("::1", 443)),
ConnectionEndpointMetadata(
/*supported_protocol_alpns=*/{"http/1.1"},
/*ech_config_list=*/{}, std::string("https_slow_ok"))),
// Non-SVCB endpoints.
ExpectServiceEndpoint(ElementsAre(MakeIPEndPoint("127.0.0.1", 443)),
ElementsAre(MakeIPEndPoint("::1", 443)))));
}
TEST_F(HostResolverServiceEndpointRequestTest, DestroyResolverWhileUpdating) {
// Using 4slow_ok not to complete transactions at once.
UseIpv4DelayedDnsRules("4slow_ok");
Requester requester = CreateRequester("https://4slow_ok");
int rv = requester.Start();
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
requester.SetOnUpdatedCallback(
base::BindLambdaForTesting([&]() { DestroyResolver(); }));
RunUntilIdle();
EXPECT_THAT(*requester.finished_result(), IsError(ERR_DNS_REQUEST_CANCELLED));
}
TEST_F(HostResolverServiceEndpointRequestTest, DestroyResolverWhileFinishing) {
UseNonDelayedDnsRules("ok");
Requester requester = CreateRequester("https://ok");
int rv = requester.Start();
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
requester.SetOnFinishedCallback(
base::BindLambdaForTesting([&]() { DestroyResolver(); }));
RunUntilIdle();
EXPECT_THAT(*requester.finished_result(), IsOk());
}
TEST_F(HostResolverServiceEndpointRequestTest, MultipleRequestsOk) {
UseIpv4DelayedDnsRules("4slow_ok");
constexpr std::string_view kHost = "https://4slow_ok";
Requester requester1 = CreateRequester(kHost);
EXPECT_THAT(requester1.Start(), IsError(ERR_IO_PENDING));
EXPECT_EQ(3u, resolver_->num_running_dispatcher_jobs_for_tests());
Requester requester2 = CreateRequester(kHost);
EXPECT_THAT(requester2.Start(), IsError(ERR_IO_PENDING));
// The second request should share the same job with the first request.
EXPECT_EQ(3u, resolver_->num_running_dispatcher_jobs_for_tests());
RunUntilIdle();
EXPECT_EQ(1u, resolver_->num_running_dispatcher_jobs_for_tests());
// Complete the delayed transaction, which finishes requests synchronously.
mock_dns_client_->CompleteDelayedTransactions();
EXPECT_THAT(*requester1.finished_result(), IsOk());
EXPECT_THAT(requester1.finished_endpoints(),
ElementsAre(ExpectServiceEndpoint(
ElementsAre(MakeIPEndPoint("127.0.0.1", 443)),
ElementsAre(MakeIPEndPoint("::1", 443)))));
EXPECT_THAT(*requester2.finished_result(), IsOk());
EXPECT_THAT(requester2.finished_endpoints(),
ElementsAre(ExpectServiceEndpoint(
ElementsAre(MakeIPEndPoint("127.0.0.1", 443)),
ElementsAre(MakeIPEndPoint("::1", 443)))));
}
TEST_F(HostResolverServiceEndpointRequestTest,
MultipleRequestsAddRequestInTheMiddleOfResolution) {
UseIpv4DelayedDnsRules("4slow_ok");
constexpr std::string_view kHost = "https://4slow_ok";
Requester requester1 = CreateRequester(kHost);
EXPECT_THAT(requester1.Start(), IsError(ERR_IO_PENDING));
EXPECT_EQ(3u, resolver_->num_running_dispatcher_jobs_for_tests());
// Partially complete transactions. Only IPv6 endpoints should be available.
RunUntilIdle();
EXPECT_EQ(1u, resolver_->num_running_dispatcher_jobs_for_tests());
ASSERT_FALSE(requester1.finished_result().has_value());
EXPECT_THAT(requester1.request()->GetEndpointResults(),
ElementsAre(ExpectServiceEndpoint(
IsEmpty(), ElementsAre(MakeIPEndPoint("::1", 443)))));
// Add a new request in the middle of resolution. The request should be
// attached to the ongoing job.
Requester requester2 = CreateRequester(kHost);
requester2.CancelRequestOnFinished();
EXPECT_THAT(requester2.Start(), IsError(ERR_IO_PENDING));
EXPECT_EQ(1u, resolver_->num_running_dispatcher_jobs_for_tests());
// The second request should have the same intermediate results as the first
// request.
ASSERT_FALSE(requester2.finished_result().has_value());
EXPECT_THAT(requester2.request()->GetEndpointResults(),
ElementsAre(ExpectServiceEndpoint(
IsEmpty(), ElementsAre(MakeIPEndPoint("::1", 443)))));
// Complete all transactions. Both requests should finish and have the same
// results.
mock_dns_client_->CompleteDelayedTransactions();
EXPECT_EQ(0u, resolver_->num_running_dispatcher_jobs_for_tests());
EXPECT_THAT(*requester1.finished_result(), IsOk());
EXPECT_THAT(requester1.finished_endpoints(),
ElementsAre(ExpectServiceEndpoint(
ElementsAre(MakeIPEndPoint("127.0.0.1", 443)),
ElementsAre(MakeIPEndPoint("::1", 443)))));
EXPECT_THAT(*requester2.finished_result(), IsOk());
EXPECT_THAT(requester2.finished_endpoints(),
ElementsAre(ExpectServiceEndpoint(
ElementsAre(MakeIPEndPoint("127.0.0.1", 443)),
ElementsAre(MakeIPEndPoint("::1", 443)))));
}
TEST_F(HostResolverServiceEndpointRequestTest,
MultipleRequestsAddAndCancelRequestInUpdatedCallback) {
UseIpv4DelayedDnsRules("4slow_ok");
constexpr std::string_view kHost = "https://4slow_ok";
Requester requester1 = CreateRequester(kHost);
Requester requester2 = CreateRequester(kHost);
requester1.SetOnUpdatedCallback(base::BindLambdaForTesting([&] {
EXPECT_THAT(requester2.Start(), IsError(ERR_IO_PENDING));
requester1.CancelRequest();
}));
EXPECT_THAT(requester1.Start(), IsError(ERR_IO_PENDING));
EXPECT_EQ(3u, resolver_->num_running_dispatcher_jobs_for_tests());
// Partially complete transactions. The update callback of the first request
// should start the second request and then cancel the first request.
RunUntilIdle();
EXPECT_EQ(1u, resolver_->num_running_dispatcher_jobs_for_tests());
ASSERT_FALSE(requester1.finished_result().has_value());
ASSERT_FALSE(requester1.request());
ASSERT_FALSE(requester2.finished_result().has_value());
EXPECT_THAT(requester2.request()->GetEndpointResults(),
ElementsAre(ExpectServiceEndpoint(
IsEmpty(), ElementsAre(MakeIPEndPoint("::1", 443)))));
// Complete all transactions. The second request should finish successfully.
mock_dns_client_->CompleteDelayedTransactions();
EXPECT_EQ(0u, resolver_->num_running_dispatcher_jobs_for_tests());
ASSERT_FALSE(requester1.finished_result().has_value());
ASSERT_FALSE(requester1.request());
EXPECT_THAT(*requester2.finished_result(), IsOk());
EXPECT_THAT(requester2.finished_endpoints(),
ElementsAre(ExpectServiceEndpoint(
ElementsAre(MakeIPEndPoint("127.0.0.1", 443)),
ElementsAre(MakeIPEndPoint("::1", 443)))));
}
TEST_F(HostResolverServiceEndpointRequestTest,
MultipleRequestsAddRequestInFinishedCallback) {
UseNonDelayedDnsRules("ok");
constexpr std::string_view kHost = "https://ok";
Requester requester1 = CreateRequester(kHost);
Requester requester2 = CreateRequester(kHost);
requester1.SetOnFinishedCallback(base::BindLambdaForTesting([&] {
// The second request should finish synchronously because it should
// share the same job as the first one and the job has finished already.
EXPECT_THAT(requester2.Start(), IsOk());
}));
EXPECT_THAT(requester1.Start(), IsError(ERR_IO_PENDING));
EXPECT_EQ(3u, resolver_->num_running_dispatcher_jobs_for_tests());
RunUntilIdle();
EXPECT_EQ(0u, resolver_->num_running_dispatcher_jobs_for_tests());
EXPECT_THAT(*requester1.finished_result(), IsOk());
EXPECT_THAT(requester1.finished_endpoints(),
ElementsAre(ExpectServiceEndpoint(
ElementsAre(MakeIPEndPoint("127.0.0.1", 443)),
ElementsAre(MakeIPEndPoint("::1", 443)))));
EXPECT_THAT(*requester2.finished_result(), IsOk());
EXPECT_THAT(requester2.finished_endpoints(),
ElementsAre(ExpectServiceEndpoint(
ElementsAre(MakeIPEndPoint("127.0.0.1", 443)),
ElementsAre(MakeIPEndPoint("::1", 443)))));
}
TEST_F(HostResolverServiceEndpointRequestTest,
MultipleRequestsCancelOneRequestOnUpdated) {
UseIpv4DelayedDnsRules("4slow_ok");
constexpr std::string_view kHost = "https://4slow_ok";
Requester requester1 = CreateRequester(kHost);
EXPECT_THAT(requester1.Start(), IsError(ERR_IO_PENDING));
EXPECT_EQ(3u, resolver_->num_running_dispatcher_jobs_for_tests());
Requester requester2 = CreateRequester(kHost);
// The second request destroys self when notified an update.
requester2.CancelRequestOnUpdated();
EXPECT_THAT(requester2.Start(), IsError(ERR_IO_PENDING));
// The second request should share the same job with the first request.
EXPECT_EQ(3u, resolver_->num_running_dispatcher_jobs_for_tests());
RunUntilIdle();
EXPECT_EQ(1u, resolver_->num_running_dispatcher_jobs_for_tests());
// Complete the delayed transaction, which finishes the first request
// synchronously.
mock_dns_client_->CompleteDelayedTransactions();
EXPECT_THAT(*requester1.finished_result(), IsOk());
EXPECT_THAT(requester1.finished_endpoints(),
ElementsAre(ExpectServiceEndpoint(
ElementsAre(MakeIPEndPoint("127.0.0.1", 443)),
ElementsAre(MakeIPEndPoint("::1", 443)))));
// The second request was destroyed so it didn't get notified.
ASSERT_FALSE(requester2.finished_result().has_value());
ASSERT_FALSE(requester2.request());
}
TEST_F(HostResolverServiceEndpointRequestTest,
MultipleRequestsCancelAllRequestOnUpdated) {
UseIpv4DelayedDnsRules("4slow_ok");
constexpr std::string_view kHost = "https://4slow_ok";
Requester requester1 = CreateRequester(kHost);
requester1.CancelRequestOnUpdated();
EXPECT_THAT(requester1.Start(), IsError(ERR_IO_PENDING));
EXPECT_EQ(3u, resolver_->num_running_dispatcher_jobs_for_tests());
Requester requester2 = CreateRequester(kHost);
requester2.CancelRequestOnUpdated();
EXPECT_THAT(requester2.Start(), IsError(ERR_IO_PENDING));
// The second request should share the same job with the first request.
EXPECT_EQ(3u, resolver_->num_running_dispatcher_jobs_for_tests());
// Complete non-delayed transactions and invoke update callbacks, which
// destroy all requests.
RunUntilIdle();
EXPECT_EQ(0u, resolver_->num_running_dispatcher_jobs_for_tests());
ASSERT_FALSE(requester1.finished_result().has_value());
ASSERT_FALSE(requester1.request());
ASSERT_FALSE(requester2.finished_result().has_value());
ASSERT_FALSE(requester2.request());
}
TEST_F(HostResolverServiceEndpointRequestTest,
MultipleRequestsCancelAllRequestOnFinished) {
UseNonDelayedDnsRules("ok");
constexpr std::string_view kHost = "https://ok";
Requester requester1 = CreateRequester(kHost);
requester1.CancelRequestOnFinished();
EXPECT_THAT(requester1.Start(), IsError(ERR_IO_PENDING));
EXPECT_EQ(3u, resolver_->num_running_dispatcher_jobs_for_tests());
Requester requester2 = CreateRequester(kHost);
requester2.CancelRequestOnFinished();
EXPECT_THAT(requester2.Start(), IsError(ERR_IO_PENDING));
// The second request should share the same job with the first request.
EXPECT_EQ(3u, resolver_->num_running_dispatcher_jobs_for_tests());
RunUntilIdle();
EXPECT_EQ(0u, resolver_->num_running_dispatcher_jobs_for_tests());
EXPECT_THAT(*requester1.finished_result(), IsOk());
EXPECT_THAT(*requester2.finished_result(), IsOk());
}
TEST_F(HostResolverServiceEndpointRequestTest, WithLegacyRequestOk) {
UseIpv4DelayedDnsRules("4slow_ok");
constexpr std::string_view kHost = "https://4slow_ok";
LegacyRequester legacy_requester = CreateLegacyRequester(kHost);
int rv = legacy_requester.Start();
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
EXPECT_EQ(3u, resolver_->num_running_dispatcher_jobs_for_tests());
Requester requester = CreateRequester(kHost);
rv = requester.Start();
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
// The request should share the same job with the legacy request.
EXPECT_EQ(3u, resolver_->num_running_dispatcher_jobs_for_tests());
// Partially complete transactions. Requests should not complete but
// the non-legacy request should provide intermediate endpoints.
RunUntilIdle();
EXPECT_EQ(1u, resolver_->num_running_dispatcher_jobs_for_tests());
ASSERT_FALSE(legacy_requester.complete_result().has_value());
ASSERT_FALSE(requester.finished_result().has_value());
ASSERT_TRUE(requester.request()->EndpointsCryptoReady());
EXPECT_THAT(requester.request()->GetEndpointResults(),
ElementsAre(ExpectServiceEndpoint(
IsEmpty(), ElementsAre(MakeIPEndPoint("::1", 443)))));
// Complete delayed transactions, which finish requests synchronously.
mock_dns_client_->CompleteDelayedTransactions();
EXPECT_THAT(*legacy_requester.complete_result(), IsOk());
EXPECT_THAT(*requester.finished_result(), IsOk());
EXPECT_THAT(requester.finished_endpoints(),
ElementsAre(ExpectServiceEndpoint(
ElementsAre(MakeIPEndPoint("127.0.0.1", 443)),
ElementsAre(MakeIPEndPoint("::1", 443)))));
}
TEST_F(HostResolverServiceEndpointRequestTest,
WithLegacyRequestDestroyResolverOnUpdated) {
UseIpv4DelayedDnsRules("4slow_ok");
constexpr std::string_view kHost = "https://4slow_ok";
LegacyRequester legacy_requester = CreateLegacyRequester(kHost);
int rv = legacy_requester.Start();
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
EXPECT_EQ(3u, resolver_->num_running_dispatcher_jobs_for_tests());
Requester requester = CreateRequester(kHost);
rv = requester.Start();
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
// The request should share the same job with the legacy request.
EXPECT_EQ(3u, resolver_->num_running_dispatcher_jobs_for_tests());
requester.SetOnUpdatedCallback(
base::BindLambdaForTesting([&]() { DestroyResolver(); }));
RunUntilIdle();
// DestroyResolver() removed the corresponding job and the legacy reqquest
// didn't get notified, but the non-legacy request got notified via the
// update callback.
ASSERT_FALSE(legacy_requester.complete_result().has_value());
EXPECT_THAT(*requester.finished_result(), IsError(ERR_DNS_REQUEST_CANCELLED));
}
TEST_F(HostResolverServiceEndpointRequestTest,
WithLegacyRequestCancelRequestOnUpdated) {
UseIpv4DelayedDnsRules("4slow_ok");
constexpr std::string_view kHost = "https://4slow_ok";
LegacyRequester legacy_requester = CreateLegacyRequester(kHost);
int rv = legacy_requester.Start();
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
EXPECT_EQ(3u, resolver_->num_running_dispatcher_jobs_for_tests());
Requester requester = CreateRequester(kHost);
requester.CancelRequestOnUpdated();
rv = requester.Start();
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
// The request should share the same job with the legacy request.
EXPECT_EQ(3u, resolver_->num_running_dispatcher_jobs_for_tests());
// Partially complete transactions to trigger the update callback on
// non-legacy request, which cancels the request itself.
RunUntilIdle();
ASSERT_FALSE(legacy_requester.complete_result().has_value());
ASSERT_FALSE(requester.request());
// Complete delayed transactions, which finish the legacy request
// synchronously. Non-legacy request was already destroyed.
mock_dns_client_->CompleteDelayedTransactions();
EXPECT_THAT(*legacy_requester.complete_result(), IsOk());
}
TEST_F(HostResolverServiceEndpointRequestTest,
WithLegacyRequestCancelLegacyRequest) {
UseNonDelayedDnsRules("ok");
constexpr std::string_view kHost = "https://ok";
LegacyRequester legacy_requester = CreateLegacyRequester(kHost);
int rv = legacy_requester.Start();
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
EXPECT_EQ(3u, resolver_->num_running_dispatcher_jobs_for_tests());
Requester requester = CreateRequester(kHost);
requester.CancelRequestOnUpdated();
rv = requester.Start();
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
// The request should share the same job with the legacy request.
EXPECT_EQ(3u, resolver_->num_running_dispatcher_jobs_for_tests());
// Cancelling legacy request should not cancel non-legacy request.
legacy_requester.CancelRequest();
ASSERT_FALSE(requester.finished_result().has_value());
EXPECT_EQ(3u, resolver_->num_running_dispatcher_jobs_for_tests());
requester.WaitForFinished();
EXPECT_EQ(0u, resolver_->num_running_dispatcher_jobs_for_tests());
EXPECT_THAT(*requester.finished_result(), IsOk());
}
} // namespace net