| Index: components/cronet/stale_host_resolver_unittest.cc
|
| diff --git a/components/cronet/stale_host_resolver_unittest.cc b/components/cronet/stale_host_resolver_unittest.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..cf3d6976952976ab9cff4f4caeafefcb07f5195e
|
| --- /dev/null
|
| +++ b/components/cronet/stale_host_resolver_unittest.cc
|
| @@ -0,0 +1,496 @@
|
| +// Copyright 2016 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 "components/cronet/stale_host_resolver.h"
|
| +
|
| +#include "base/callback_helpers.h"
|
| +#include "base/logging.h"
|
| +#include "base/memory/ptr_util.h"
|
| +#include "base/memory/ref_counted.h"
|
| +#include "base/message_loop/message_loop.h"
|
| +#include "base/run_loop.h"
|
| +#include "base/test/test_timeouts.h"
|
| +#include "base/values.h"
|
| +#include "components/cronet/url_request_context_config.h"
|
| +#include "net/base/net_errors.h"
|
| +#include "net/cert/cert_verifier.h"
|
| +#include "net/dns/host_resolver_proc.h"
|
| +#include "net/http/http_network_session.h"
|
| +#include "net/proxy/proxy_config.h"
|
| +#include "net/proxy/proxy_config_service_fixed.h"
|
| +#include "net/url_request/url_request_context.h"
|
| +#include "net/url_request/url_request_context_builder.h"
|
| +#include "testing/gtest/include/gtest/gtest.h"
|
| +
|
| +namespace cronet {
|
| +
|
| +namespace {
|
| +
|
| +const char kHostname[] = "example.com";
|
| +const char kCacheAddress[] = "1.1.1.1";
|
| +const char kNetworkAddress[] = "2.2.2.2";
|
| +const char kUninitializedAddress[] = "3.3.3.3";
|
| +const int kCacheEntryTTLSec = 300;
|
| +
|
| +const int kNoStaleDelaySec = 0;
|
| +const int kLongStaleDelaySec = 3600;
|
| +const uint16_t kPort = 12345;
|
| +
|
| +const int kAgeFreshSec = 0;
|
| +const int kAgeExpiredSec = kCacheEntryTTLSec * 2;
|
| +
|
| +// How long to wait for resolve calls to return. If the tests are working
|
| +// correctly, we won't end up waiting this long -- it's just a backup.
|
| +const int kWaitTimeoutSec = 1;
|
| +
|
| +net::AddressList MakeAddressList(const char* ip_address_str) {
|
| + net::IPAddress address;
|
| + bool rv = address.AssignFromIPLiteral(ip_address_str);
|
| + DCHECK(rv);
|
| +
|
| + net::AddressList address_list;
|
| + address_list.push_back(net::IPEndPoint(address, 0u));
|
| + return address_list;
|
| +}
|
| +
|
| +class MockHostResolverProc : public net::HostResolverProc {
|
| + public:
|
| + MockHostResolverProc() : HostResolverProc(nullptr) {}
|
| +
|
| + ~MockHostResolverProc() override {}
|
| +
|
| + int Resolve(const std::string& hostname,
|
| + net::AddressFamily address_family,
|
| + net::HostResolverFlags host_resolver_flags,
|
| + net::AddressList* address_list,
|
| + int* os_error) override {
|
| + *address_list = MakeAddressList(kNetworkAddress);
|
| + return net::OK;
|
| + }
|
| +};
|
| +
|
| +class StaleHostResolverTest : public testing::Test {
|
| + protected:
|
| + StaleHostResolverTest()
|
| + : mock_proc_(new MockHostResolverProc()),
|
| + resolver_(nullptr),
|
| + resolve_pending_(false),
|
| + resolve_complete_(false) {}
|
| +
|
| + ~StaleHostResolverTest() {}
|
| +
|
| + void SetStaleDelay(int stale_delay_sec) {
|
| + DCHECK(!resolver_);
|
| +
|
| + options_.delay = base::TimeDelta::FromSeconds(stale_delay_sec);
|
| + }
|
| +
|
| + void SetStaleUsability(int max_expired_time_sec,
|
| + int max_stale_uses,
|
| + bool allow_other_network) {
|
| + DCHECK(!resolver_);
|
| +
|
| + options_.max_expired_time =
|
| + base::TimeDelta::FromSeconds(max_expired_time_sec);
|
| + options_.max_stale_uses = max_stale_uses;
|
| + options_.allow_other_network = allow_other_network;
|
| + }
|
| +
|
| + void CreateResolver() {
|
| + DCHECK(!resolver_);
|
| +
|
| + std::unique_ptr<net::HostResolverImpl> inner_resolver(
|
| + net::HostResolver::CreateDefaultResolverImpl(nullptr));
|
| +
|
| + net::HostResolverImpl::ProcTaskParams proc_params(mock_proc_.get(), 1u);
|
| + inner_resolver->set_proc_params_for_test(proc_params);
|
| +
|
| + stale_resolver_ = base::WrapUnique(
|
| + new StaleHostResolver(std::move(inner_resolver), options_));
|
| + resolver_ = stale_resolver_.get();
|
| + }
|
| +
|
| + void DestroyResolver() {
|
| + DCHECK(stale_resolver_);
|
| +
|
| + stale_resolver_.reset();
|
| + resolver_ = nullptr;
|
| + }
|
| +
|
| + void SetResolver(net::HostResolver* resolver) {
|
| + DCHECK(!resolver_);
|
| +
|
| + resolver_ = resolver;
|
| + }
|
| +
|
| + void ClearResolver() {
|
| + DCHECK(resolver_);
|
| + DCHECK(!stale_resolver_);
|
| +
|
| + resolver_ = nullptr;
|
| + }
|
| +
|
| + // Creates a cache entry for |kHostname| that is |age_sec| seconds old.
|
| + void CreateCacheEntry(int age_sec) {
|
| + DCHECK(resolver_);
|
| + DCHECK(resolver_->GetHostCache());
|
| +
|
| + base::TimeDelta ttl(base::TimeDelta::FromSeconds(kCacheEntryTTLSec));
|
| + net::HostCache::Key key(kHostname, net::ADDRESS_FAMILY_IPV4, 0);
|
| + net::HostCache::Entry entry(net::OK, MakeAddressList(kCacheAddress), ttl);
|
| + base::TimeDelta age = base::TimeDelta::FromSeconds(age_sec);
|
| + base::TimeTicks then = base::TimeTicks::Now() - age;
|
| + resolver_->GetHostCache()->Set(key, entry, then, ttl);
|
| + }
|
| +
|
| + void OnNetworkChange() {
|
| + DCHECK(resolver_);
|
| + DCHECK(resolver_->GetHostCache());
|
| +
|
| + resolver_->GetHostCache()->OnNetworkChange();
|
| + }
|
| +
|
| + void LookupStale() {
|
| + DCHECK(resolver_);
|
| + DCHECK(resolver_->GetHostCache());
|
| +
|
| + net::HostCache::Key key(kHostname, net::ADDRESS_FAMILY_IPV4, 0);
|
| + base::TimeTicks now = base::TimeTicks::Now();
|
| + const net::HostCache::Entry* entry;
|
| + net::HostCache::EntryStaleness stale;
|
| + entry = resolver_->GetHostCache()->LookupStale(key, now, &stale);
|
| + EXPECT_TRUE(entry);
|
| + EXPECT_TRUE(stale.is_stale());
|
| + }
|
| +
|
| + void Resolve() {
|
| + DCHECK(resolver_);
|
| + EXPECT_FALSE(resolve_pending_);
|
| +
|
| + net::HostResolver::RequestInfo info(net::HostPortPair(kHostname, kPort));
|
| + info.set_address_family(net::ADDRESS_FAMILY_IPV4);
|
| +
|
| + resolve_pending_ = true;
|
| + resolve_complete_ = false;
|
| + resolve_addresses_ = MakeAddressList(kUninitializedAddress);
|
| + resolve_error_ = net::ERR_UNEXPECTED;
|
| +
|
| + int rv =
|
| + resolver_->Resolve(info, net::DEFAULT_PRIORITY, &resolve_addresses_,
|
| + base::Bind(&StaleHostResolverTest::OnResolveComplete,
|
| + base::Unretained(this)),
|
| + &request_, net::BoundNetLog());
|
| + if (rv != net::ERR_IO_PENDING) {
|
| + resolve_pending_ = false;
|
| + resolve_complete_ = true;
|
| + resolve_error_ = rv;
|
| + }
|
| + }
|
| +
|
| + void WaitForResolve() {
|
| + if (!resolve_pending_)
|
| + return;
|
| +
|
| + base::RunLoop run_loop;
|
| +
|
| + // Run until resolve completes or timeout.
|
| + resolve_closure_ = run_loop.QuitWhenIdleClosure();
|
| + base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
|
| + FROM_HERE, run_loop.QuitWhenIdleClosure(),
|
| + base::TimeDelta::FromSeconds(kWaitTimeoutSec));
|
| + run_loop.Run();
|
| + }
|
| +
|
| + void WaitForIdle() {
|
| + base::RunLoop run_loop;
|
| +
|
| + base::ThreadTaskRunnerHandle::Get()->PostTask(
|
| + FROM_HERE, run_loop.QuitWhenIdleClosure());
|
| + run_loop.Run();
|
| + }
|
| +
|
| + void Cancel() {
|
| + DCHECK(resolver_);
|
| + EXPECT_TRUE(resolve_pending_);
|
| +
|
| + delete request_.release();
|
| +
|
| + resolve_pending_ = false;
|
| + }
|
| +
|
| + void OnResolveComplete(int error) {
|
| + EXPECT_TRUE(resolve_pending_);
|
| +
|
| + request_.reset();
|
| +
|
| + resolve_error_ = error;
|
| + resolve_pending_ = false;
|
| + resolve_complete_ = true;
|
| +
|
| + if (!resolve_closure_.is_null())
|
| + base::ResetAndReturn(&resolve_closure_).Run();
|
| + }
|
| +
|
| + bool resolve_complete() const { return resolve_complete_; }
|
| + int resolve_error() const { return resolve_error_; }
|
| + const net::AddressList& resolve_addresses() const {
|
| + return resolve_addresses_;
|
| + }
|
| +
|
| + private:
|
| + // Needed for HostResolver to run HostResolverProc callbacks.
|
| + base::MessageLoopForIO message_loop_for_io_;
|
| + scoped_refptr<MockHostResolverProc> mock_proc_;
|
| +
|
| + net::HostResolver* resolver_;
|
| + StaleHostResolver::StaleOptions options_;
|
| + std::unique_ptr<StaleHostResolver> stale_resolver_;
|
| +
|
| + base::TimeTicks now_;
|
| + std::unique_ptr<net::HostResolver::Request> request_;
|
| + bool resolve_pending_;
|
| + bool resolve_complete_;
|
| + net::AddressList resolve_addresses_;
|
| + int resolve_error_;
|
| +
|
| + base::Closure resolve_closure_;
|
| +};
|
| +
|
| +// Make sure that test harness can be created and destroyed without crashing.
|
| +TEST_F(StaleHostResolverTest, Null) {}
|
| +
|
| +// Make sure that resolver can be created and destroyed without crashing.
|
| +TEST_F(StaleHostResolverTest, Create) {
|
| + CreateResolver();
|
| +}
|
| +
|
| +TEST_F(StaleHostResolverTest, Network) {
|
| + CreateResolver();
|
| +
|
| + Resolve();
|
| + WaitForResolve();
|
| +
|
| + EXPECT_TRUE(resolve_complete());
|
| + EXPECT_EQ(net::OK, resolve_error());
|
| + EXPECT_EQ(1u, resolve_addresses().size());
|
| + EXPECT_EQ(kNetworkAddress, resolve_addresses()[0].ToStringWithoutPort());
|
| +}
|
| +
|
| +TEST_F(StaleHostResolverTest, FreshCache) {
|
| + CreateResolver();
|
| + CreateCacheEntry(kAgeFreshSec);
|
| +
|
| + Resolve();
|
| +
|
| + EXPECT_TRUE(resolve_complete());
|
| + EXPECT_EQ(net::OK, resolve_error());
|
| + EXPECT_EQ(1u, resolve_addresses().size());
|
| + EXPECT_EQ(kCacheAddress, resolve_addresses()[0].ToStringWithoutPort());
|
| +
|
| + WaitForIdle();
|
| +}
|
| +
|
| +TEST_F(StaleHostResolverTest, StaleCache) {
|
| + SetStaleDelay(kNoStaleDelaySec);
|
| + CreateResolver();
|
| + CreateCacheEntry(kAgeExpiredSec);
|
| +
|
| + 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());
|
| +}
|
| +
|
| +TEST_F(StaleHostResolverTest, NetworkWithStaleCache) {
|
| + SetStaleDelay(kLongStaleDelaySec);
|
| + CreateResolver();
|
| + CreateCacheEntry(kAgeExpiredSec);
|
| +
|
| + Resolve();
|
| + WaitForResolve();
|
| +
|
| + EXPECT_TRUE(resolve_complete());
|
| + EXPECT_EQ(net::OK, resolve_error());
|
| + EXPECT_EQ(1u, resolve_addresses().size());
|
| + EXPECT_EQ(kNetworkAddress, resolve_addresses()[0].ToStringWithoutPort());
|
| +}
|
| +
|
| +TEST_F(StaleHostResolverTest, CancelWithNoCache) {
|
| + SetStaleDelay(kNoStaleDelaySec);
|
| + CreateResolver();
|
| +
|
| + Resolve();
|
| +
|
| + Cancel();
|
| +
|
| + EXPECT_FALSE(resolve_complete());
|
| +
|
| + // Make sure there's no lingering |OnResolveComplete()| callback waiting.
|
| + WaitForIdle();
|
| +}
|
| +
|
| +TEST_F(StaleHostResolverTest, CancelWithStaleCache) {
|
| + SetStaleDelay(kLongStaleDelaySec);
|
| + CreateResolver();
|
| + CreateCacheEntry(kAgeExpiredSec);
|
| +
|
| + Resolve();
|
| +
|
| + Cancel();
|
| +
|
| + EXPECT_FALSE(resolve_complete());
|
| +
|
| + // Make sure there's no lingering |OnResolveComplete()| callback waiting.
|
| + WaitForIdle();
|
| +}
|
| +
|
| +// CancelWithFreshCache makes no sense; the request would've returned
|
| +// synchronously.
|
| +
|
| +TEST_F(StaleHostResolverTest, StaleUsability) {
|
| + const struct {
|
| + int max_expired_time_sec;
|
| + int max_stale_uses;
|
| + bool allow_other_network;
|
| +
|
| + int age_sec;
|
| + int stale_use;
|
| + int network_changes;
|
| +
|
| + bool usable;
|
| + } kUsabilityTestCases[] = {
|
| + // Fresh data always accepted.
|
| + {0, 0, true, -1, 1, 0, true},
|
| + {1, 1, false, -1, 1, 0, true},
|
| +
|
| + // Unlimited expired time accepts non-zero time.
|
| + {0, 0, true, 1, 1, 0, true},
|
| +
|
| + // Limited expired time accepts before but not after limit.
|
| + {2, 0, true, 1, 1, 0, true},
|
| + {2, 0, true, 3, 1, 0, false},
|
| +
|
| + // Unlimited stale uses accepts first and later uses.
|
| + {2, 0, true, 1, 1, 0, true},
|
| + {2, 0, true, 1, 9, 0, true},
|
| +
|
| + // Limited stale uses accepts up to and including limit.
|
| + {2, 2, true, 1, 1, 0, true},
|
| + {2, 2, true, 1, 2, 0, true},
|
| + {2, 2, true, 1, 3, 0, false},
|
| + {2, 2, true, 1, 9, 0, false},
|
| +
|
| + // Allowing other networks accepts zero or more network changes.
|
| + {2, 0, true, 1, 1, 0, true},
|
| + {2, 0, true, 1, 1, 1, true},
|
| + {2, 0, true, 1, 1, 9, true},
|
| +
|
| + // Disallowing other networks only accepts zero network changes.
|
| + {2, 0, false, 1, 1, 0, true},
|
| + {2, 0, false, 1, 1, 1, false},
|
| + {2, 0, false, 1, 1, 9, false},
|
| + };
|
| +
|
| + SetStaleDelay(kNoStaleDelaySec);
|
| +
|
| + for (size_t i = 0; i < arraysize(kUsabilityTestCases); ++i) {
|
| + const auto& test_case = kUsabilityTestCases[i];
|
| +
|
| + SetStaleUsability(test_case.max_expired_time_sec, test_case.max_stale_uses,
|
| + test_case.allow_other_network);
|
| + CreateResolver();
|
| + CreateCacheEntry(kCacheEntryTTLSec + test_case.age_sec);
|
| + for (int j = 0; j < test_case.network_changes; ++j)
|
| + OnNetworkChange();
|
| + for (int j = 0; j < test_case.stale_use - 1; ++j)
|
| + LookupStale();
|
| +
|
| + Resolve();
|
| + WaitForResolve();
|
| + EXPECT_TRUE(resolve_complete()) << i;
|
| + EXPECT_EQ(net::OK, resolve_error()) << i;
|
| + EXPECT_EQ(1u, resolve_addresses().size()) << i;
|
| + {
|
| + const char* expected = test_case.usable ? kCacheAddress : kNetworkAddress;
|
| + EXPECT_EQ(expected, resolve_addresses()[0].ToStringWithoutPort()) << i;
|
| + }
|
| +
|
| + DestroyResolver();
|
| + }
|
| +}
|
| +
|
| +TEST_F(StaleHostResolverTest, CreatedByContext) {
|
| + URLRequestContextConfig config(
|
| + // Enable QUIC.
|
| + true,
|
| + // QUIC User Agent ID.
|
| + "Default QUIC User Agent ID",
|
| + // Enable SPDY.
|
| + true,
|
| + // Enable SDCH.
|
| + false,
|
| + // Type of http cache.
|
| + URLRequestContextConfig::HttpCacheType::DISK,
|
| + // Max size of http cache in bytes.
|
| + 1024000,
|
| + // Disable caching for HTTP responses. Other information may be stored in
|
| + // the cache.
|
| + false,
|
| + // Storage path for http cache and cookie storage.
|
| + "/data/data/org.chromium.net/app_cronet_test/test_storage",
|
| + // User-Agent request header field.
|
| + "fake agent",
|
| + // JSON encoded experimental options.
|
| + "{\"AsyncDNS\":{\"enable\":false},"
|
| + "\"StaleDNS\":{\"enable\":true,"
|
| + "\"delay_ms\":0,"
|
| + "\"max_expired_time_ms\":0,"
|
| + "\"max_stale_uses\":0}}",
|
| + // Data reduction proxy key.
|
| + "",
|
| + // Data reduction proxy.
|
| + "",
|
| + // Fallback data reduction proxy.
|
| + "",
|
| + // Data reduction proxy secure proxy check URL.
|
| + "",
|
| + // MockCertVerifier to use for testing purposes.
|
| + std::unique_ptr<net::CertVerifier>(),
|
| + // Enable network quality estimator.
|
| + false,
|
| + // Enable Public Key Pinning bypass for local trust anchors.
|
| + true,
|
| + // Certificate verifier cache data.
|
| + "");
|
| +
|
| + net::URLRequestContextBuilder builder;
|
| + net::NetLog net_log;
|
| + config.ConfigureURLRequestContextBuilder(&builder, &net_log, nullptr);
|
| + // Set a ProxyConfigService to avoid DCHECK failure when building.
|
| + builder.set_proxy_config_service(base::WrapUnique(
|
| + new net::ProxyConfigServiceFixed(net::ProxyConfig::CreateDirect())));
|
| + std::unique_ptr<net::URLRequestContext> context(builder.Build());
|
| +
|
| + // Duplicate StaleCache test case to ensure StaleHostResolver was created:
|
| +
|
| + // Note: Experimental config above sets 0ms stale delay.
|
| + SetResolver(context->host_resolver());
|
| + CreateCacheEntry(kAgeExpiredSec);
|
| +
|
| + Resolve();
|
| + EXPECT_FALSE(resolve_complete());
|
| + WaitForResolve();
|
| +
|
| + EXPECT_TRUE(resolve_complete());
|
| + EXPECT_EQ(net::OK, resolve_error());
|
| + EXPECT_EQ(1u, resolve_addresses().size());
|
| + EXPECT_EQ(kCacheAddress, resolve_addresses()[0].ToStringWithoutPort());
|
| +}
|
| +
|
| +} // namespace
|
| +
|
| +} // namespace cronet
|
|
|