Index: net/dns/host_cache.cc |
diff --git a/net/dns/host_cache.cc b/net/dns/host_cache.cc |
index 0026c02176c03c8c5dc28ba463000d4d8699d890..c1dbbda9b0afb67f356c1be16b14f83463af71d9 100644 |
--- a/net/dns/host_cache.cc |
+++ b/net/dns/host_cache.cc |
@@ -4,15 +4,19 @@ |
#include "net/dns/host_cache.h" |
+#include <utility> |
+ |
#include "base/logging.h" |
#include "base/memory/ptr_util.h" |
#include "base/metrics/field_trial.h" |
#include "base/metrics/histogram_macros.h" |
#include "base/strings/string_number_conversions.h" |
#include "base/trace_event/trace_event.h" |
+#include "base/values.h" |
#include "net/base/net_errors.h" |
#include "net/base/trace_constants.h" |
#include "net/dns/dns_util.h" |
+#include "net/log/net_log.h" |
namespace net { |
@@ -27,6 +31,31 @@ namespace { |
#define CACHE_HISTOGRAM_ENUM(name, value, max) \ |
UMA_HISTOGRAM_ENUMERATION("DNS.HostCache." name, value, max) |
+// String constants for dictionary keys. |
+const char kHostnameKey[] = "hostname"; |
+const char kAddressFamilyKey[] = "address_family"; |
+const char kFlagsKey[] = "flags"; |
+const char kExpirationKey[] = "expiration"; |
+const char kTtlKey[] = "ttl"; |
+const char kNetworkChangesKey[] = "network_changes"; |
+const char kErrorKey[] = "error"; |
+const char kAddressesKey[] = "addresses"; |
+ |
+bool AddressListFromListValue(base::ListValue* value, AddressList* list) { |
+ list->clear(); |
+ for (base::ListValue::const_iterator it = value->begin(); it != value->end(); |
+ it++) { |
+ IPAddress address; |
+ std::string addr_string; |
+ if (!it->GetAsString(&addr_string) || |
+ !address.AssignFromIPLiteral(addr_string)) { |
+ return false; |
+ } |
+ list->push_back(IPEndPoint(address, 0)); |
+ } |
+ return true; |
+} |
+ |
} // namespace |
// Used in histograms; do not modify existing values. |
@@ -80,6 +109,18 @@ HostCache::Entry::Entry(const HostCache::Entry& entry, |
total_hits_(0), |
stale_hits_(0) {} |
+HostCache::Entry::Entry(int error, |
+ const AddressList& addresses, |
+ base::TimeTicks expires, |
+ int network_changes) |
+ : error_(error), |
+ addresses_(addresses), |
+ ttl_(base::TimeDelta::FromSeconds(-1)), |
+ expires_(expires), |
+ network_changes_(network_changes), |
+ total_hits_(0), |
+ stale_hits_(0) {} |
+ |
bool HostCache::Entry::IsStale(base::TimeTicks now, int network_changes) const { |
EntryStaleness stale; |
stale.expired_by = now - expires_; |
@@ -182,10 +223,13 @@ void HostCache::Set(const Key& key, |
RecordSet(SET_INSERT, now, nullptr, entry); |
} |
+ AddEntry(Key(key), Entry(entry, now, ttl, network_changes_)); |
+} |
+ |
+void HostCache::AddEntry(const Key& key, const Entry& entry) { |
DCHECK_GT(max_entries_, size()); |
DCHECK_EQ(0u, entries_.count(key)); |
- entries_.insert( |
- std::make_pair(Key(key), Entry(entry, now, ttl, network_changes_))); |
+ entries_.insert(std::make_pair(key, entry)); |
DCHECK_GE(max_entries_, size()); |
} |
@@ -221,6 +265,110 @@ void HostCache::ClearForHosts( |
} |
} |
+std::unique_ptr<base::ListValue> HostCache::GetAsListValue( |
+ bool include_staleness) const { |
+ std::unique_ptr<base::ListValue> entry_list(new base::ListValue()); |
+ |
+ for (const auto& pair : entries_) { |
+ const Key& key = pair.first; |
+ const Entry& entry = pair.second; |
+ |
+ std::unique_ptr<base::DictionaryValue> entry_dict( |
+ new base::DictionaryValue()); |
+ |
+ entry_dict->SetString(kHostnameKey, key.hostname); |
+ entry_dict->SetInteger(kAddressFamilyKey, |
+ static_cast<int>(key.address_family)); |
+ entry_dict->SetInteger(kFlagsKey, key.host_resolver_flags); |
+ |
+ if (include_staleness) { |
+ entry_dict->SetString(kExpirationKey, |
+ NetLog::TickCountToString(entry.expires())); |
+ entry_dict->SetInteger(kTtlKey, entry.ttl().InMilliseconds()); |
+ entry_dict->SetInteger(kNetworkChangesKey, entry.network_changes()); |
+ } else { |
+ // Convert expiration time in TimeTicks to Time for serialization, using a |
+ // string because base::Value doesn't handle 64-bit integers. |
+ base::Time expiration_time = |
+ base::Time::Now() - (base::TimeTicks::Now() - entry.expires()); |
+ entry_dict->SetString( |
+ kExpirationKey, |
+ base::Int64ToString(expiration_time.ToInternalValue())); |
+ } |
+ |
+ if (entry.error() != OK) { |
+ entry_dict->SetInteger(kErrorKey, entry.error()); |
+ } else { |
+ const AddressList& addresses = entry.addresses(); |
+ // Append all of the resolved addresses. |
+ auto addresses_value = base::MakeUnique<base::ListValue>(); |
+ for (size_t i = 0; i < addresses.size(); ++i) |
+ addresses_value->AppendString(addresses[i].ToStringWithoutPort()); |
+ entry_dict->SetList(kAddressesKey, std::move(addresses_value)); |
+ } |
+ |
+ entry_list->Append(std::move(entry_dict)); |
+ } |
+ |
+ return entry_list; |
+} |
+ |
+// TODO(mgersh): Add histograms to track failures. |
+bool HostCache::RestoreFromListValue(base::ListValue& old_cache) { |
+ for (base::ListValue::iterator it = old_cache.begin(); it != old_cache.end(); |
+ it++) { |
+ base::DictionaryValue* entry_dict; |
+ if (!it->GetAsDictionary(&entry_dict)) |
+ return false; |
+ |
+ std::string hostname; |
+ int address_family; |
+ HostResolverFlags flags; |
+ int error = OK; |
+ std::string expiration; |
+ base::ListValue empty_list; |
+ base::ListValue* addresses_value = &empty_list; |
+ AddressList address_list; |
+ |
+ if (!entry_dict->GetString(kHostnameKey, &hostname) || |
+ !entry_dict->GetInteger(kFlagsKey, &flags) || |
+ !entry_dict->GetInteger(kAddressFamilyKey, &address_family) || |
+ !entry_dict->GetString(kExpirationKey, &expiration)) { |
+ return false; |
+ } |
+ |
+ // Only one of these fields should be in the dictionary. |
+ if (!entry_dict->GetInteger(kErrorKey, &error) && |
+ !entry_dict->GetList(kAddressesKey, &addresses_value)) { |
+ return false; |
+ } |
+ |
+ int64_t time_internal; |
+ if (!base::StringToInt64(expiration, &time_internal)) |
+ return false; |
+ |
+ base::TimeTicks expiration_time = |
+ base::TimeTicks::Now() - |
+ (base::Time::Now() - base::Time::FromInternalValue(time_internal)); |
+ |
+ Key key(hostname, static_cast<AddressFamily>(address_family), flags); |
+ if (error == OK && |
+ !AddressListFromListValue(addresses_value, &address_list)) { |
+ return false; |
+ } |
+ |
+ // If the key is already in the cache, assume it's more recent and don't |
+ // replace the entry. If the cache is already full, don't bother |
+ // prioritizing what to evict, just stop restoring. |
+ auto found = entries_.find(key); |
+ if (found == entries_.end() && size() < max_entries_) { |
+ AddEntry(key, Entry(error, address_list, expiration_time, |
+ network_changes_ - 1)); |
+ } |
+ } |
+ return true; |
+} |
+ |
size_t HostCache::size() const { |
DCHECK(CalledOnValidThread()); |
return entries_.size(); |