Commit eb15d678 authored by Paul Jensen's avatar Paul Jensen Committed by Commit Bot

[Cronet] Add option to use stale DNS when network resolver returns ERR_NAME_NOT_RESOLVED

Sometimes resolving a host name will result in ERR_NAME_NOT_RESOLVED even
though the host still should resolve.  This change adds an option to use
stale DNS results in these cases.

Bug: 875845

Cq-Include-Trybots: master.tryserver.chromium.android:android_cronet_tester;master.tryserver.chromium.mac:ios-simulator-cronet
Change-Id: I02b7a122bb9236f9ff47c73af8e4803315dcd382
Reviewed-on: https://chromium-review.googlesource.com/1179998
Commit-Queue: Paul Jensen <pauljensen@chromium.org>
Reviewed-by: default avatarMisha Efimov <mef@chromium.org>
Cr-Commit-Position: refs/heads/master@{#584772}
parent 54feed0d
...@@ -36,6 +36,9 @@ enum RequestOutcome { ...@@ -36,6 +36,9 @@ enum RequestOutcome {
// Request canceled; there was usable stale data. // Request canceled; there was usable stale data.
CANCELED_WITH_STALE = 5, CANCELED_WITH_STALE = 5,
// Stale data returned; network got ERR_NAME_NOT_RESOLVED.
STALE_INSTEAD_OF_NETWORK_NAME_NOT_RESOLVED = 6,
MAX_REQUEST_OUTCOME MAX_REQUEST_OUTCOME
}; };
...@@ -84,7 +87,10 @@ bool StaleEntryIsUsable(const StaleHostResolver::StaleOptions& options, ...@@ -84,7 +87,10 @@ bool StaleEntryIsUsable(const StaleHostResolver::StaleOptions& options,
// network data, or stale cached data. // network data, or stale cached data.
class StaleHostResolver::RequestImpl { class StaleHostResolver::RequestImpl {
public: public:
explicit RequestImpl(const base::TickClock* tick_clock); // If |use_stale_on_name_not_resolved| and network resolution returns
// ERR_NAME_NOT_RESOLVED, use stale result if available.
explicit RequestImpl(const base::TickClock* tick_clock,
bool use_stale_on_name_not_resolved);
~RequestImpl(); ~RequestImpl();
// A callback for the caller to decide whether a stale entry is usable or not. // A callback for the caller to decide whether a stale entry is usable or not.
...@@ -149,7 +155,10 @@ class StaleHostResolver::RequestImpl { ...@@ -149,7 +155,10 @@ class StaleHostResolver::RequestImpl {
void MaybeDeleteThis(); void MaybeDeleteThis();
void RecordSynchronousRequest(); void RecordSynchronousRequest();
void RecordNetworkRequest(int error, bool returned_stale_data); void RecordNetworkRequest(
int error,
bool returned_stale_data,
bool returned_stale_data_instead_of_network_name_not_resolved);
void RecordCanceledRequest(); void RecordCanceledRequest();
// The address list passed into |Start()| to be filled in when the request // The address list passed into |Start()| to be filled in when the request
...@@ -189,9 +198,14 @@ class StaleHostResolver::RequestImpl { ...@@ -189,9 +198,14 @@ class StaleHostResolver::RequestImpl {
// Handle that caller can use to cancel the request before it returns. // Handle that caller can use to cancel the request before it returns.
// Owned by the caller; cleared via |OnHandleDestroyed()| when destroyed. // Owned by the caller; cleared via |OnHandleDestroyed()| when destroyed.
Handle* handle_; Handle* handle_;
// If |use_stale_on_name_not_resolved| and network resolution returns
// ERR_NAME_NOT_RESOLVED, use stale result if available.
const bool use_stale_on_name_not_resolved_;
}; };
StaleHostResolver::RequestImpl::RequestImpl(const base::TickClock* tick_clock) StaleHostResolver::RequestImpl::RequestImpl(const base::TickClock* tick_clock,
bool use_stale_on_name_not_resolved)
: result_addresses_(nullptr), : result_addresses_(nullptr),
returning_result_(false), returning_result_(false),
stale_error_(net::ERR_DNS_CACHE_MISS), stale_error_(net::ERR_DNS_CACHE_MISS),
...@@ -199,7 +213,8 @@ StaleHostResolver::RequestImpl::RequestImpl(const base::TickClock* tick_clock) ...@@ -199,7 +213,8 @@ StaleHostResolver::RequestImpl::RequestImpl(const base::TickClock* tick_clock)
tick_clock_(tick_clock), tick_clock_(tick_clock),
restore_size_(0), restore_size_(0),
current_size_(0), current_size_(0),
handle_(nullptr) {} handle_(nullptr),
use_stale_on_name_not_resolved_(use_stale_on_name_not_resolved) {}
StaleHostResolver::RequestImpl::~RequestImpl() {} StaleHostResolver::RequestImpl::~RequestImpl() {}
...@@ -288,12 +303,23 @@ void StaleHostResolver::RequestImpl::OnNetworkRequestComplete(int error) { ...@@ -288,12 +303,23 @@ void StaleHostResolver::RequestImpl::OnNetworkRequestComplete(int error) {
DCHECK(have_network_request()); DCHECK(have_network_request());
network_request_.reset(); network_request_.reset();
RecordNetworkRequest(error, /* returned_stale_data = */ have_returned()); bool return_stale_data_instead_of_network_name_not_resolved =
!have_returned() && use_stale_on_name_not_resolved_ &&
error == net::ERR_NAME_NOT_RESOLVED && have_stale_data();
RecordNetworkRequest(
error, /* returned_stale_data = */ have_returned() ||
return_stale_data_instead_of_network_name_not_resolved,
return_stale_data_instead_of_network_name_not_resolved);
if (!have_returned()) { if (!have_returned()) {
if (have_stale_data()) if (have_stale_data())
stale_timer_.Stop(); stale_timer_.Stop();
ReturnResult(error, network_addresses_); if (return_stale_data_instead_of_network_name_not_resolved) {
ReturnResult(stale_error_, stale_addresses_);
} else {
ReturnResult(error, network_addresses_);
}
} }
if (!have_handle()) if (!have_handle())
...@@ -342,7 +368,8 @@ void StaleHostResolver::RequestImpl::RecordSynchronousRequest() { ...@@ -342,7 +368,8 @@ void StaleHostResolver::RequestImpl::RecordSynchronousRequest() {
void StaleHostResolver::RequestImpl::RecordNetworkRequest( void StaleHostResolver::RequestImpl::RecordNetworkRequest(
int error, int error,
bool returned_stale_data) { bool returned_stale_data,
bool returned_stale_data_instead_of_network_name_not_resolved) {
if (have_stale_data()) if (have_stale_data())
RecordTimeDelta(tick_clock_->NowTicks(), stale_timer_.desired_run_time()); RecordTimeDelta(tick_clock_->NowTicks(), stale_timer_.desired_run_time());
...@@ -352,7 +379,11 @@ void StaleHostResolver::RequestImpl::RecordNetworkRequest( ...@@ -352,7 +379,11 @@ void StaleHostResolver::RequestImpl::RecordNetworkRequest(
} }
if (returned_stale_data) { if (returned_stale_data) {
RecordRequestOutcome(STALE_BEFORE_NETWORK); if (returned_stale_data_instead_of_network_name_not_resolved) {
RecordRequestOutcome(STALE_INSTEAD_OF_NETWORK_NAME_NOT_RESOLVED);
} else {
RecordRequestOutcome(STALE_BEFORE_NETWORK);
}
} else if (have_stale_data()) { } else if (have_stale_data()) {
RecordRequestOutcome(NETWORK_WITH_STALE); RecordRequestOutcome(NETWORK_WITH_STALE);
RecordCacheSizes(restore_size_, current_size_); RecordCacheSizes(restore_size_, current_size_);
...@@ -369,7 +400,9 @@ void StaleHostResolver::RequestImpl::RecordCanceledRequest() { ...@@ -369,7 +400,9 @@ void StaleHostResolver::RequestImpl::RecordCanceledRequest() {
} }
StaleHostResolver::StaleOptions::StaleOptions() StaleHostResolver::StaleOptions::StaleOptions()
: allow_other_network(false), max_stale_uses(0) {} : allow_other_network(false),
max_stale_uses(0),
use_stale_on_name_not_resolved(false) {}
StaleHostResolver::StaleHostResolver( StaleHostResolver::StaleHostResolver(
std::unique_ptr<net::HostResolverImpl> inner_resolver, std::unique_ptr<net::HostResolverImpl> inner_resolver,
...@@ -400,7 +433,8 @@ int StaleHostResolver::Resolve(const RequestInfo& info, ...@@ -400,7 +433,8 @@ int StaleHostResolver::Resolve(const RequestInfo& info,
DCHECK(tick_clock_); DCHECK(tick_clock_);
StaleHostResolver::RequestImpl::StaleEntryUsableCallback usable_callback = StaleHostResolver::RequestImpl::StaleEntryUsableCallback usable_callback =
base::Bind(&StaleEntryIsUsable, options_); base::Bind(&StaleEntryIsUsable, options_);
RequestImpl* request = new RequestImpl(tick_clock_); RequestImpl* request =
new RequestImpl(tick_clock_, options_.use_stale_on_name_not_resolved);
int rv = request->Start(inner_resolver_.get(), info, priority, addresses, int rv = request->Start(inner_resolver_.get(), info, priority, addresses,
std::move(callback), out_req, net_log, std::move(callback), out_req, net_log,
usable_callback, options_.delay); usable_callback, options_.delay);
......
...@@ -50,6 +50,10 @@ class StaleHostResolver : public net::HostResolver { ...@@ -50,6 +50,10 @@ class StaleHostResolver : public net::HostResolver {
// If positive, the maximum number of times a stale entry can be used. If // If positive, the maximum number of times a stale entry can be used. If
// zero, there is no limit. // zero, there is no limit.
int max_stale_uses; int max_stale_uses;
// If network resolution returns ERR_NAME_NOT_RESOLVED, use stale result if
// available.
bool use_stale_on_name_not_resolved;
}; };
// Creates a StaleHostResolver that uses |inner_resolver| for actual // Creates a StaleHostResolver that uses |inner_resolver| for actual
......
...@@ -81,7 +81,9 @@ std::unique_ptr<net::DnsClient> CreateMockDnsClientForHosts() { ...@@ -81,7 +81,9 @@ std::unique_ptr<net::DnsClient> CreateMockDnsClientForHosts() {
class MockHostResolverProc : public net::HostResolverProc { class MockHostResolverProc : public net::HostResolverProc {
public: public:
MockHostResolverProc() : HostResolverProc(nullptr) {} // |result| is the net error code to return from resolution attempts.
MockHostResolverProc(int result)
: HostResolverProc(nullptr), result_(result) {}
int Resolve(const std::string& hostname, int Resolve(const std::string& hostname,
net::AddressFamily address_family, net::AddressFamily address_family,
...@@ -89,11 +91,15 @@ class MockHostResolverProc : public net::HostResolverProc { ...@@ -89,11 +91,15 @@ class MockHostResolverProc : public net::HostResolverProc {
net::AddressList* address_list, net::AddressList* address_list,
int* os_error) override { int* os_error) override {
*address_list = MakeAddressList(kNetworkAddress); *address_list = MakeAddressList(kNetworkAddress);
return net::OK; return result_;
} }
protected: protected:
~MockHostResolverProc() override {} ~MockHostResolverProc() override {}
private:
// Result code to return from Resolve().
const int result_;
}; };
class StaleHostResolverTest : public testing::Test { class StaleHostResolverTest : public testing::Test {
...@@ -101,7 +107,7 @@ class StaleHostResolverTest : public testing::Test { ...@@ -101,7 +107,7 @@ class StaleHostResolverTest : public testing::Test {
StaleHostResolverTest() StaleHostResolverTest()
: scoped_task_environment_( : scoped_task_environment_(
base::test::ScopedTaskEnvironment::MainThreadType::IO), base::test::ScopedTaskEnvironment::MainThreadType::IO),
mock_proc_(new MockHostResolverProc()), mock_proc_(new MockHostResolverProc(net::OK)),
resolver_(nullptr), resolver_(nullptr),
resolve_pending_(false), resolve_pending_(false),
resolve_complete_(false) { resolve_complete_(false) {
...@@ -117,6 +123,12 @@ class StaleHostResolverTest : public testing::Test { ...@@ -117,6 +123,12 @@ class StaleHostResolverTest : public testing::Test {
options_.delay = base::TimeDelta::FromSeconds(stale_delay_sec); options_.delay = base::TimeDelta::FromSeconds(stale_delay_sec);
} }
void SetUseStaleOnNameNotResolved() {
DCHECK(!resolver_);
options_.use_stale_on_name_not_resolved = true;
}
void SetStaleUsability(int max_expired_time_sec, void SetStaleUsability(int max_expired_time_sec,
int max_stale_uses, int max_stale_uses,
bool allow_other_network) { bool allow_other_network) {
...@@ -128,6 +140,12 @@ class StaleHostResolverTest : public testing::Test { ...@@ -128,6 +140,12 @@ class StaleHostResolverTest : public testing::Test {
options_.allow_other_network = allow_other_network; options_.allow_other_network = allow_other_network;
} }
void SetNetResult(int result) {
DCHECK(!resolver_);
mock_proc_ = new MockHostResolverProc(result);
}
std::unique_ptr<net::HostResolverImpl> CreateMockInnerResolverWithDnsClient( std::unique_ptr<net::HostResolverImpl> CreateMockInnerResolverWithDnsClient(
std::unique_ptr<net::DnsClient> dns_client) { std::unique_ptr<net::DnsClient> dns_client) {
std::unique_ptr<net::HostResolverImpl> inner_resolver( std::unique_ptr<net::HostResolverImpl> inner_resolver(
...@@ -376,6 +394,39 @@ TEST_F(StaleHostResolverTest, MAYBE_StaleCache) { ...@@ -376,6 +394,39 @@ TEST_F(StaleHostResolverTest, MAYBE_StaleCache) {
EXPECT_EQ(kCacheAddress, resolve_addresses()[0].ToStringWithoutPort()); EXPECT_EQ(kCacheAddress, resolve_addresses()[0].ToStringWithoutPort());
} }
// Ensure that |use_stale_on_name_not_resolved| causes stale results to be
// returned when ERR_NAME_NOT_RESOLVED is returned from network resolution.
TEST_F(StaleHostResolverTest, StaleCacheNameNotResolvedEnabled) {
SetStaleDelay(kLongStaleDelaySec);
SetUseStaleOnNameNotResolved();
SetNetResult(net::ERR_NAME_NOT_RESOLVED);
CreateResolver();
CreateCacheEntry(kAgeExpiredSec, net::OK);
Resolve();
WaitForResolve();
EXPECT_TRUE(resolve_complete());
EXPECT_EQ(net::OK, resolve_error());
EXPECT_EQ(1u, resolve_addresses().size());
EXPECT_EQ(kCacheAddress, resolve_addresses()[0].ToStringWithoutPort());
}
// Ensure that without |use_stale_on_name_not_resolved| network resolution
// failing causes StaleHostResolver jobs to fail with the same error code.
TEST_F(StaleHostResolverTest, StaleCacheNameNotResolvedDisabled) {
SetStaleDelay(kLongStaleDelaySec);
SetNetResult(net::ERR_NAME_NOT_RESOLVED);
CreateResolver();
CreateCacheEntry(kAgeExpiredSec, net::OK);
Resolve();
WaitForResolve();
EXPECT_TRUE(resolve_complete());
EXPECT_EQ(net::ERR_NAME_NOT_RESOLVED, resolve_error());
}
TEST_F(StaleHostResolverTest, NetworkWithStaleCache) { TEST_F(StaleHostResolverTest, NetworkWithStaleCache) {
SetStaleDelay(kLongStaleDelaySec); SetStaleDelay(kLongStaleDelaySec);
CreateResolver(); CreateResolver();
......
...@@ -107,6 +107,10 @@ const char kStaleDnsPersist[] = "persist_to_disk"; ...@@ -107,6 +107,10 @@ const char kStaleDnsPersist[] = "persist_to_disk";
// cache persistence. Default value is one minute. Only relevant if // cache persistence. Default value is one minute. Only relevant if
// "persist_to_disk" is true. // "persist_to_disk" is true.
const char kStaleDnsPersistTimer[] = "persist_delay_ms"; const char kStaleDnsPersistTimer[] = "persist_delay_ms";
// Name of boolean to allow use of stale DNS results when network resolver
// returns ERR_NAME_NOT_RESOLVED.
const char kStaleDnsUseStaleOnNameNotResolved[] =
"use_stale_on_name_not_resolved";
// Rules to override DNS resolution. Intended for testing. // Rules to override DNS resolution. Intended for testing.
// See explanation of format in net/dns/mapped_host_resolver.h. // See explanation of format in net/dns/mapped_host_resolver.h.
...@@ -434,6 +438,12 @@ void URLRequestContextConfig::ParseAndSetExperimentalOptions( ...@@ -434,6 +438,12 @@ void URLRequestContextConfig::ParseAndSetExperimentalOptions(
int persist_timer; int persist_timer;
if (stale_dns_args->GetInteger(kStaleDnsPersistTimer, &persist_timer)) if (stale_dns_args->GetInteger(kStaleDnsPersistTimer, &persist_timer))
host_cache_persistence_delay_ms = persist_timer; host_cache_persistence_delay_ms = persist_timer;
bool use_stale_on_name_not_resolved;
if (stale_dns_args->GetBoolean(kStaleDnsUseStaleOnNameNotResolved,
&use_stale_on_name_not_resolved)) {
stale_dns_options.use_stale_on_name_not_resolved =
use_stale_on_name_not_resolved;
}
} }
} else if (it.key() == kHostResolverRulesFieldTrialName) { } else if (it.key() == kHostResolverRulesFieldTrialName) {
const base::DictionaryValue* host_resolver_rules_args = nullptr; const base::DictionaryValue* host_resolver_rules_args = nullptr;
......
...@@ -10692,6 +10692,8 @@ Called by update_net_error_codes.py.--> ...@@ -10692,6 +10692,8 @@ Called by update_net_error_codes.py.-->
<int value="3" label="Returned stale cached result; network was too slow."/> <int value="3" label="Returned stale cached result; network was too slow."/>
<int value="4" label="Canceled; no stale cached result was available."/> <int value="4" label="Canceled; no stale cached result was available."/>
<int value="5" label="Canceled; stale cached result was available."/> <int value="5" label="Canceled; stale cached result was available."/>
<int value="6"
label="Returned stale cached result; network got NAME_NOT_RESOLVED"/>
</enum> </enum>
<enum name="DNSEmptyAddressListAndNoError"> <enum name="DNSEmptyAddressListAndNoError">
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment