blob: a8e08f08a10cf0bd1f714bf5028d2adfe9037bfd [file] [log] [blame]
// Copyright (c) 2012 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 "net/dns/mock_host_resolver.h"
#include <utility>
#include <vector>
#include "base/bind.h"
#include "base/callback_helpers.h"
#include "base/location.h"
#include "base/logging.h"
#include "base/memory/ref_counted.h"
#include "base/single_thread_task_runner.h"
#include "base/stl_util.h"
#include "base/strings/pattern.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "base/threading/platform_thread.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/time/default_tick_clock.h"
#include "base/time/tick_clock.h"
#include "net/base/ip_address.h"
#include "net/base/ip_endpoint.h"
#include "net/base/net_errors.h"
#include "net/base/test_completion_callback.h"
#include "net/dns/host_cache.h"
#if defined(OS_WIN)
#include "net/base/winsock_init.h"
#endif
namespace net {
namespace {
// Cache size for the MockCachingHostResolver.
const unsigned kMaxCacheEntries = 100;
// TTL for the successful resolutions. Failures are not cached.
const unsigned kCacheEntryTTLSeconds = 60;
} // namespace
int ParseAddressList(const std::string& host_list,
const std::string& canonical_name,
AddressList* addrlist) {
*addrlist = AddressList();
addrlist->set_canonical_name(canonical_name);
for (const base::StringPiece& address : base::SplitStringPiece(
host_list, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL)) {
IPAddress ip_address;
if (!ip_address.AssignFromIPLiteral(address)) {
LOG(WARNING) << "Not a supported IP literal: " << address.as_string();
return ERR_UNEXPECTED;
}
addrlist->push_back(IPEndPoint(ip_address, 0));
}
return OK;
}
class MockHostResolverBase::RequestImpl
: public HostResolver::ResolveHostRequest {
public:
RequestImpl(const HostPortPair& request_host,
const base::Optional<ResolveHostParameters>& optional_parameters,
base::WeakPtr<MockHostResolverBase> resolver)
: request_host_(request_host),
parameters_(optional_parameters ? optional_parameters.value()
: ResolveHostParameters()),
host_resolver_flags_(ParametersToHostResolverFlags(parameters_)),
id_(0),
resolver_(resolver),
complete_(false) {}
~RequestImpl() override {
if (id_ > 0) {
if (resolver_)
resolver_->DetachRequest(id_);
id_ = 0;
resolver_ = nullptr;
}
}
int Start(CompletionOnceCallback callback) override {
DCHECK(callback);
// Start() may only be called once per request.
DCHECK_EQ(0u, id_);
DCHECK(!complete_);
DCHECK(!callback_);
// Parent HostResolver must still be alive to call Start().
DCHECK(resolver_);
int rv = resolver_->Resolve(this);
DCHECK(!complete_);
if (rv == ERR_IO_PENDING) {
DCHECK_GT(id_, 0u);
callback_ = std::move(callback);
} else {
DCHECK_EQ(0u, id_);
complete_ = true;
}
return rv;
}
const base::Optional<AddressList>& GetAddressResults() const override {
DCHECK(complete_);
return address_results_;
}
#if defined(COBALT_QUIC46)
const base::Optional<HostCache::EntryStaleness>& GetStaleInfo()
const override {
NOTIMPLEMENTED() << "MockHostResolverBase::RequestImpl::GetStaleInfo() "
"always return empty base::Optional.";
return stale_info_;
}
void ChangeRequestPriority(RequestPriority priority) override {
priority_ = priority;
}
RequestPriority priority() const { return priority_; }
#endif
void set_address_results(const AddressList& address_results) {
// Should only be called at most once and before request is marked
// completed.
DCHECK(!complete_);
DCHECK(!address_results_);
DCHECK(!parameters_.is_speculative);
address_results_ = address_results;
}
void OnAsyncCompleted(size_t id, int error) {
DCHECK_EQ(id_, id);
id_ = 0;
DCHECK(!complete_);
complete_ = true;
DCHECK(callback_);
std::move(callback_).Run(error);
}
const HostPortPair& request_host() const { return request_host_; }
const ResolveHostParameters& parameters() const { return parameters_; }
int host_resolver_flags() const { return host_resolver_flags_; }
size_t id() { return id_; }
void set_id(size_t id) {
DCHECK_GT(id, 0u);
DCHECK_EQ(0u, id_);
id_ = id;
}
bool complete() { return complete_; }
private:
const HostPortPair request_host_;
const ResolveHostParameters parameters_;
int host_resolver_flags_;
base::Optional<AddressList> address_results_;
// Used while stored with the resolver for async resolution. Otherwise 0.
size_t id_;
CompletionOnceCallback callback_;
// Use a WeakPtr as the resolver may be destroyed while there are still
// outstanding request objects.
base::WeakPtr<MockHostResolverBase> resolver_;
bool complete_;
#if defined(COBALT_QUIC46)
base::Optional<HostCache::EntryStaleness> stale_info_;
RequestPriority priority_;
#endif
DISALLOW_COPY_AND_ASSIGN(RequestImpl);
};
class MockHostResolverBase::LegacyRequestImpl : public HostResolver::Request {
public:
explicit LegacyRequestImpl(std::unique_ptr<RequestImpl> inner_request)
: inner_request_(std::move(inner_request)) {
DCHECK_EQ(0u, inner_request_->id());
DCHECK(!inner_request_->complete());
}
~LegacyRequestImpl() override {}
void ChangeRequestPriority(RequestPriority priority) override {}
int Start() {
return inner_request_->Start(base::BindOnce(
&LegacyRequestImpl::LegacyApiCallback, base::Unretained(this)));
}
void AssignCallback(CompletionOnceCallback callback,
AddressList* addresses_result_ptr) {
DCHECK(callback);
DCHECK(addresses_result_ptr);
DCHECK_GT(inner_request_->id(), 0u);
DCHECK(!inner_request_->complete());
callback_ = std::move(callback);
addresses_result_ptr_ = addresses_result_ptr;
}
const RequestImpl& inner_request() const { return *inner_request_; }
private:
void LegacyApiCallback(int error) {
// Must call AssignCallback() before async results.
DCHECK(callback_);
if (error == OK && !inner_request_->parameters().is_speculative) {
// Legacy API does not allow non-address results (eg TXT), so AddressList
// is always expected to be present on OK.
DCHECK(inner_request_->GetAddressResults());
*addresses_result_ptr_ = inner_request_->GetAddressResults().value();
}
addresses_result_ptr_ = nullptr;
std::move(callback_).Run(error);
}
const std::unique_ptr<RequestImpl> inner_request_;
CompletionOnceCallback callback_;
// This is a caller-provided pointer and should not be used once |callback_|
// is invoked.
AddressList* addresses_result_ptr_;
DISALLOW_COPY_AND_ASSIGN(LegacyRequestImpl);
};
MockHostResolverBase::~MockHostResolverBase() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
DCHECK(requests_.empty());
}
std::unique_ptr<HostResolver::ResolveHostRequest>
MockHostResolverBase::CreateRequest(
const HostPortPair& host,
const NetLogWithSource& source_net_log,
const base::Optional<ResolveHostParameters>& optional_parameters) {
return std::make_unique<RequestImpl>(host, optional_parameters, AsWeakPtr());
}
int MockHostResolverBase::Resolve(const RequestInfo& info,
RequestPriority priority,
AddressList* addresses,
CompletionOnceCallback callback,
std::unique_ptr<Request>* out_request,
const NetLogWithSource& net_log) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
DCHECK(out_request);
auto request = std::make_unique<RequestImpl>(
info.host_port_pair(), RequestInfoToResolveHostParameters(info, priority),
AsWeakPtr());
auto wrapped_request =
std::make_unique<LegacyRequestImpl>(std::move(request));
int rv = wrapped_request->Start();
if (rv == OK) {
DCHECK(wrapped_request->inner_request().GetAddressResults());
*addresses = wrapped_request->inner_request().GetAddressResults().value();
} else if (rv == ERR_IO_PENDING) {
wrapped_request->AssignCallback(std::move(callback), addresses);
*out_request = std::move(wrapped_request);
}
return rv;
}
int MockHostResolverBase::ResolveFromCache(const RequestInfo& info,
AddressList* addresses,
const NetLogWithSource& net_log) {
num_resolve_from_cache_++;
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
next_request_id_++;
int rv = ResolveFromIPLiteralOrCache(
info.host_port_pair(), info.address_family(), info.host_resolver_flags(),
HostResolverSource::ANY, info.allow_cached_response(), addresses);
return rv;
}
int MockHostResolverBase::ResolveStaleFromCache(
const RequestInfo& info,
AddressList* addresses,
HostCache::EntryStaleness* stale_info,
const NetLogWithSource& net_log) {
num_resolve_from_cache_++;
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
next_request_id_++;
int rv = ResolveFromIPLiteralOrCache(
info.host_port_pair(), info.address_family(), info.host_resolver_flags(),
HostResolverSource::ANY, info.allow_cached_response(), addresses,
stale_info);
return rv;
}
void MockHostResolverBase::DetachRequest(size_t id) {
auto it = requests_.find(id);
CHECK(it != requests_.end());
requests_.erase(it);
}
HostCache* MockHostResolverBase::GetHostCache() {
return cache_.get();
}
bool MockHostResolverBase::HasCached(
base::StringPiece hostname,
HostCache::Entry::Source* source_out,
HostCache::EntryStaleness* stale_out) const {
if (!cache_)
return false;
return cache_->HasEntry(hostname, source_out, stale_out);
}
void MockHostResolverBase::ResolveAllPending() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
DCHECK(ondemand_mode_);
for (auto i = requests_.begin(); i != requests_.end(); ++i) {
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::Bind(&MockHostResolverBase::ResolveNow, AsWeakPtr(), i->first));
}
}
#if defined(COBALT_QUIC46)
MockHostResolverBase::RequestImpl* MockHostResolverBase::request(size_t id) {
RequestMap::iterator request = requests_.find(id);
DCHECK(request != requests_.end());
return (*request).second;
}
RequestPriority MockHostResolverBase::request_priority(size_t id) {
DCHECK(request(id));
return request(id)->priority();
}
#endif
// start id from 1 to distinguish from NULL RequestHandle
MockHostResolverBase::MockHostResolverBase(bool use_caching)
: last_request_priority_(DEFAULT_PRIORITY),
synchronous_mode_(false),
ondemand_mode_(false),
next_request_id_(1),
num_resolve_(0),
num_resolve_from_cache_(0),
tick_clock_(base::DefaultTickClock::GetInstance()) {
rules_map_[HostResolverSource::ANY] = CreateCatchAllHostResolverProc();
rules_map_[HostResolverSource::SYSTEM] = CreateCatchAllHostResolverProc();
rules_map_[HostResolverSource::DNS] = CreateCatchAllHostResolverProc();
rules_map_[HostResolverSource::MULTICAST_DNS] =
CreateCatchAllHostResolverProc();
if (use_caching) {
cache_.reset(new HostCache(kMaxCacheEntries));
}
}
int MockHostResolverBase::Resolve(RequestImpl* request) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
last_request_priority_ = request->parameters().initial_priority;
num_resolve_++;
AddressList addresses;
int rv = ResolveFromIPLiteralOrCache(
request->request_host(),
DnsQueryTypeToAddressFamily(request->parameters().dns_query_type),
request->host_resolver_flags(), request->parameters().source,
request->parameters().allow_cached_response, &addresses);
if (rv == OK && !request->parameters().is_speculative)
request->set_address_results(addresses);
if (rv != ERR_DNS_CACHE_MISS)
return rv;
// Just like the real resolver, refuse to do anything with invalid
// hostnames.
if (!IsValidDNSDomain(request->request_host().host()))
return ERR_NAME_NOT_RESOLVED;
if (synchronous_mode_) {
int rv = ResolveProc(
request->request_host(),
DnsQueryTypeToAddressFamily(request->parameters().dns_query_type),
request->host_resolver_flags(), request->parameters().source,
&addresses);
if (rv == OK && !request->parameters().is_speculative)
request->set_address_results(addresses);
return rv;
}
// Store the request for asynchronous resolution
size_t id = next_request_id_++;
request->set_id(id);
requests_[id] = request;
if (!ondemand_mode_) {
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::BindOnce(&MockHostResolverBase::ResolveNow, AsWeakPtr(), id));
}
return ERR_IO_PENDING;
}
int MockHostResolverBase::ResolveFromIPLiteralOrCache(
const HostPortPair& host,
AddressFamily requested_address_family,
HostResolverFlags flags,
HostResolverSource source,
bool allow_cache,
AddressList* addresses,
HostCache::EntryStaleness* stale_info) {
IPAddress ip_address;
if (ip_address.AssignFromIPLiteral(host.host())) {
// This matches the behavior HostResolverImpl.
if (requested_address_family != ADDRESS_FAMILY_UNSPECIFIED &&
requested_address_family != GetAddressFamily(ip_address)) {
return ERR_NAME_NOT_RESOLVED;
}
*addresses = AddressList::CreateFromIPAddress(ip_address, host.port());
if (flags & HOST_RESOLVER_CANONNAME)
addresses->SetDefaultCanonicalName();
return OK;
}
int rv = ERR_DNS_CACHE_MISS;
if (cache_.get() && allow_cache) {
HostCache::Key key(host.host(), requested_address_family, flags, source);
const HostCache::Entry* entry;
if (stale_info)
entry = cache_->LookupStale(key, tick_clock_->NowTicks(), stale_info);
else
entry = cache_->Lookup(key, tick_clock_->NowTicks());
if (entry) {
rv = entry->error();
if (rv == OK)
*addresses = AddressList::CopyWithPort(entry->addresses(), host.port());
}
}
return rv;
}
int MockHostResolverBase::ResolveProc(const HostPortPair& host,
AddressFamily requested_address_family,
HostResolverFlags flags,
HostResolverSource source,
AddressList* addresses) {
DCHECK(rules_map_.find(source) != rules_map_.end());
AddressList addr;
int rv = rules_map_[source]->Resolve(host.host(), requested_address_family,
flags, &addr, nullptr);
if (cache_.get()) {
HostCache::Key key(host.host(), requested_address_family, flags, source);
// Storing a failure with TTL 0 so that it overwrites previous value.
base::TimeDelta ttl;
if (rv == OK)
ttl = base::TimeDelta::FromSeconds(kCacheEntryTTLSeconds);
cache_->Set(key,
HostCache::Entry(rv, addr, HostCache::Entry::SOURCE_UNKNOWN),
tick_clock_->NowTicks(), ttl);
}
if (rv == OK)
*addresses = AddressList::CopyWithPort(addr, host.port());
return rv;
}
void MockHostResolverBase::ResolveNow(size_t id) {
auto it = requests_.find(id);
if (it == requests_.end())
return; // was canceled
RequestImpl* req = it->second;
requests_.erase(it);
AddressList addresses;
int error = ResolveProc(
req->request_host(),
DnsQueryTypeToAddressFamily(req->parameters().dns_query_type),
req->host_resolver_flags(), req->parameters().source, &addresses);
if (error == OK && !req->parameters().is_speculative)
req->set_address_results(addresses);
req->OnAsyncCompleted(id, error);
}
//-----------------------------------------------------------------------------
RuleBasedHostResolverProc::Rule::Rule(ResolverType resolver_type,
const std::string& host_pattern,
AddressFamily address_family,
HostResolverFlags host_resolver_flags,
const std::string& replacement,
const std::string& canonical_name,
int latency_ms)
: resolver_type(resolver_type),
host_pattern(host_pattern),
address_family(address_family),
host_resolver_flags(host_resolver_flags),
replacement(replacement),
canonical_name(canonical_name),
latency_ms(latency_ms) {}
RuleBasedHostResolverProc::Rule::Rule(const Rule& other) = default;
RuleBasedHostResolverProc::RuleBasedHostResolverProc(HostResolverProc* previous)
: HostResolverProc(previous), modifications_allowed_(true) {}
void RuleBasedHostResolverProc::AddRule(const std::string& host_pattern,
const std::string& replacement) {
AddRuleForAddressFamily(host_pattern, ADDRESS_FAMILY_UNSPECIFIED,
replacement);
}
void RuleBasedHostResolverProc::AddRuleForAddressFamily(
const std::string& host_pattern,
AddressFamily address_family,
const std::string& replacement) {
DCHECK(!replacement.empty());
HostResolverFlags flags = HOST_RESOLVER_LOOPBACK_ONLY |
HOST_RESOLVER_DEFAULT_FAMILY_SET_DUE_TO_NO_IPV6;
Rule rule(Rule::kResolverTypeSystem, host_pattern, address_family, flags,
replacement, std::string(), 0);
AddRuleInternal(rule);
}
void RuleBasedHostResolverProc::AddRuleWithFlags(
const std::string& host_pattern,
const std::string& replacement,
HostResolverFlags flags,
const std::string& canonical_name) {
DCHECK(!replacement.empty());
Rule rule(Rule::kResolverTypeSystem, host_pattern, ADDRESS_FAMILY_UNSPECIFIED,
flags, replacement, canonical_name, 0);
AddRuleInternal(rule);
}
void RuleBasedHostResolverProc::AddIPLiteralRule(
const std::string& host_pattern,
const std::string& ip_literal,
const std::string& canonical_name) {
// Literals are always resolved to themselves by HostResolverImpl,
// consequently we do not support remapping them.
IPAddress ip_address;
DCHECK(!ip_address.AssignFromIPLiteral(host_pattern));
HostResolverFlags flags = HOST_RESOLVER_LOOPBACK_ONLY |
HOST_RESOLVER_DEFAULT_FAMILY_SET_DUE_TO_NO_IPV6;
if (!canonical_name.empty())
flags |= HOST_RESOLVER_CANONNAME;
Rule rule(Rule::kResolverTypeIPLiteral, host_pattern,
ADDRESS_FAMILY_UNSPECIFIED, flags, ip_literal, canonical_name, 0);
AddRuleInternal(rule);
}
void RuleBasedHostResolverProc::AddRuleWithLatency(
const std::string& host_pattern,
const std::string& replacement,
int latency_ms) {
DCHECK(!replacement.empty());
HostResolverFlags flags = HOST_RESOLVER_LOOPBACK_ONLY |
HOST_RESOLVER_DEFAULT_FAMILY_SET_DUE_TO_NO_IPV6;
Rule rule(Rule::kResolverTypeSystem, host_pattern, ADDRESS_FAMILY_UNSPECIFIED,
flags, replacement, std::string(), latency_ms);
AddRuleInternal(rule);
}
void RuleBasedHostResolverProc::AllowDirectLookup(
const std::string& host_pattern) {
HostResolverFlags flags = HOST_RESOLVER_LOOPBACK_ONLY |
HOST_RESOLVER_DEFAULT_FAMILY_SET_DUE_TO_NO_IPV6;
Rule rule(Rule::kResolverTypeSystem, host_pattern, ADDRESS_FAMILY_UNSPECIFIED,
flags, std::string(), std::string(), 0);
AddRuleInternal(rule);
}
void RuleBasedHostResolverProc::AddSimulatedFailure(
const std::string& host_pattern) {
HostResolverFlags flags = HOST_RESOLVER_LOOPBACK_ONLY |
HOST_RESOLVER_DEFAULT_FAMILY_SET_DUE_TO_NO_IPV6;
Rule rule(Rule::kResolverTypeFail, host_pattern, ADDRESS_FAMILY_UNSPECIFIED,
flags, std::string(), std::string(), 0);
AddRuleInternal(rule);
}
void RuleBasedHostResolverProc::ClearRules() {
CHECK(modifications_allowed_);
base::AutoLock lock(rule_lock_);
rules_.clear();
}
void RuleBasedHostResolverProc::DisableModifications() {
modifications_allowed_ = false;
}
RuleBasedHostResolverProc::RuleList RuleBasedHostResolverProc::GetRules() {
RuleList rv;
{
base::AutoLock lock(rule_lock_);
rv = rules_;
}
return rv;
}
int RuleBasedHostResolverProc::Resolve(const std::string& host,
AddressFamily address_family,
HostResolverFlags host_resolver_flags,
AddressList* addrlist,
int* os_error) {
base::AutoLock lock(rule_lock_);
RuleList::iterator r;
for (r = rules_.begin(); r != rules_.end(); ++r) {
bool matches_address_family =
r->address_family == ADDRESS_FAMILY_UNSPECIFIED ||
r->address_family == address_family;
// Ignore HOST_RESOLVER_SYSTEM_ONLY, since it should have no impact on
// whether a rule matches.
HostResolverFlags flags = host_resolver_flags & ~HOST_RESOLVER_SYSTEM_ONLY;
// Flags match if all of the bitflags in host_resolver_flags are enabled
// in the rule's host_resolver_flags. However, the rule may have additional
// flags specified, in which case the flags should still be considered a
// match.
bool matches_flags = (r->host_resolver_flags & flags) == flags;
if (matches_flags && matches_address_family &&
base::MatchPattern(host, r->host_pattern)) {
if (r->latency_ms != 0) {
base::PlatformThread::Sleep(
base::TimeDelta::FromMilliseconds(r->latency_ms));
}
// Remap to a new host.
const std::string& effective_host =
r->replacement.empty() ? host : r->replacement;
// Apply the resolving function to the remapped hostname.
switch (r->resolver_type) {
case Rule::kResolverTypeFail:
return ERR_NAME_NOT_RESOLVED;
case Rule::kResolverTypeSystem:
#if defined(OS_WIN)
EnsureWinsockInit();
#endif
return SystemHostResolverCall(effective_host, address_family,
host_resolver_flags, addrlist,
os_error);
case Rule::kResolverTypeIPLiteral: {
AddressList raw_addr_list;
int result = ParseAddressList(
effective_host,
!r->canonical_name.empty() ? r->canonical_name : host,
&raw_addr_list);
// Filter out addresses with the wrong family.
*addrlist = AddressList();
for (const auto& address : raw_addr_list) {
if (address_family == ADDRESS_FAMILY_UNSPECIFIED ||
address_family == address.GetFamily()) {
addrlist->push_back(address);
}
}
addrlist->set_canonical_name(raw_addr_list.canonical_name());
if (result == OK && addrlist->empty())
return ERR_NAME_NOT_RESOLVED;
return result;
}
default:
NOTREACHED();
return ERR_UNEXPECTED;
}
}
}
return ResolveUsingPrevious(host, address_family, host_resolver_flags,
addrlist, os_error);
}
RuleBasedHostResolverProc::~RuleBasedHostResolverProc() = default;
void RuleBasedHostResolverProc::AddRuleInternal(const Rule& rule) {
Rule fixed_rule = rule;
// SystemResolverProc expects valid DNS addresses.
// So for kResolverTypeSystem rules:
// * If the replacement is an IP address, switch to an IP literal rule.
// * If it's a non-empty invalid domain name, switch to a fail rule (Empty
// domain names mean use a direct lookup).
if (fixed_rule.resolver_type == Rule::kResolverTypeSystem) {
IPAddress ip_address;
bool valid_address = ip_address.AssignFromIPLiteral(fixed_rule.replacement);
if (valid_address) {
fixed_rule.resolver_type = Rule::kResolverTypeIPLiteral;
} else if (!fixed_rule.replacement.empty() &&
!IsValidDNSDomain(fixed_rule.replacement)) {
// TODO(mmenke): Can this be replaced with a DCHECK instead?
fixed_rule.resolver_type = Rule::kResolverTypeFail;
}
}
CHECK(modifications_allowed_);
base::AutoLock lock(rule_lock_);
rules_.push_back(fixed_rule);
}
RuleBasedHostResolverProc* CreateCatchAllHostResolverProc() {
RuleBasedHostResolverProc* catchall = new RuleBasedHostResolverProc(NULL);
// Note that IPv6 lookups fail.
catchall->AddIPLiteralRule("*", "127.0.0.1", "localhost");
// Next add a rules-based layer the use controls.
return new RuleBasedHostResolverProc(catchall);
}
//-----------------------------------------------------------------------------
// Implementation of both the Request and ResolveHostRequest interfaces. Both
// can be implemented from the same class as this implementation does not really
// do anything except track cancellations, which for both interfaces is when the
// request is destroyed after being started.
class HangingHostResolver::RequestImpl
: public HostResolver::Request,
public HostResolver::ResolveHostRequest {
public:
RequestImpl(base::WeakPtr<HangingHostResolver> resolver, bool is_running)
: resolver_(resolver), is_running_(is_running) {}
~RequestImpl() override {
if (is_running_ && resolver_)
resolver_->num_cancellations_++;
}
int Start(CompletionOnceCallback callback) override {
DCHECK(resolver_);
is_running_ = true;
return ERR_IO_PENDING;
}
const base::Optional<AddressList>& GetAddressResults() const override {
IMMEDIATE_CRASH();
return fake_address;
}
void ChangeRequestPriority(RequestPriority priority) override {}
#if defined(COBALT_QUIC46)
const base::Optional<HostCache::EntryStaleness>& GetStaleInfo()
const override {
NOTIMPLEMENTED() << "MockHostResolverBase::RequestImpl::GetStaleInfo() "
"always return empty base::Optional.";
return stale_info_;
}
#endif
private:
// Use a WeakPtr as the resolver may be destroyed while there are still
// outstanding request objects.
base::WeakPtr<HangingHostResolver> resolver_;
bool is_running_;
#if defined(STARBOARD)
base::Optional<AddressList> fake_address;
#endif
#if defined(COBALT_QUIC46)
base::Optional<HostCache::EntryStaleness> stale_info_;
#endif
DISALLOW_COPY_AND_ASSIGN(RequestImpl);
};
HangingHostResolver::HangingHostResolver() = default;
HangingHostResolver::~HangingHostResolver() = default;
std::unique_ptr<HostResolver::ResolveHostRequest>
HangingHostResolver::CreateRequest(
const HostPortPair& host,
const NetLogWithSource& source_net_log,
const base::Optional<ResolveHostParameters>& optional_parameters) {
return std::make_unique<RequestImpl>(weak_ptr_factory_.GetWeakPtr(),
false /* started */);
}
int HangingHostResolver::Resolve(const RequestInfo& info,
RequestPriority priority,
AddressList* addresses,
CompletionOnceCallback callback,
std::unique_ptr<Request>* request,
const NetLogWithSource& net_log) {
*request = std::make_unique<RequestImpl>(weak_ptr_factory_.GetWeakPtr(),
true /* started */);
return ERR_IO_PENDING;
}
int HangingHostResolver::ResolveFromCache(const RequestInfo& info,
AddressList* addresses,
const NetLogWithSource& net_log) {
return ERR_DNS_CACHE_MISS;
}
int HangingHostResolver::ResolveStaleFromCache(
const RequestInfo& info,
AddressList* addresses,
HostCache::EntryStaleness* stale_info,
const NetLogWithSource& net_log) {
return ERR_DNS_CACHE_MISS;
}
bool HangingHostResolver::HasCached(
base::StringPiece hostname,
HostCache::Entry::Source* source_out,
HostCache::EntryStaleness* stale_out) const {
return false;
}
//-----------------------------------------------------------------------------
ScopedDefaultHostResolverProc::ScopedDefaultHostResolverProc() = default;
ScopedDefaultHostResolverProc::ScopedDefaultHostResolverProc(
HostResolverProc* proc) {
Init(proc);
}
ScopedDefaultHostResolverProc::~ScopedDefaultHostResolverProc() {
HostResolverProc* old_proc =
HostResolverProc::SetDefault(previous_proc_.get());
// The lifetimes of multiple instances must be nested.
CHECK_EQ(old_proc, current_proc_.get());
}
void ScopedDefaultHostResolverProc::Init(HostResolverProc* proc) {
current_proc_ = proc;
previous_proc_ = HostResolverProc::SetDefault(current_proc_.get());
current_proc_->SetLastProc(previous_proc_.get());
}
} // namespace net