| Index: net/base/sdch_manager.cc
|
| diff --git a/net/base/sdch_manager.cc b/net/base/sdch_manager.cc
|
| index af5e6ee3fa7424ff7e9990d9a667940f81907521..1aa9627e2a8be4ab6ec60462b5c6021bd899b4a9 100644
|
| --- a/net/base/sdch_manager.cc
|
| +++ b/net/base/sdch_manager.cc
|
| @@ -9,6 +9,7 @@
|
| #include "base/metrics/histogram.h"
|
| #include "base/strings/string_number_conversions.h"
|
| #include "base/strings/string_util.h"
|
| +#include "base/time/default_clock.h"
|
| #include "base/values.h"
|
| #include "crypto/sha2.h"
|
| #include "net/base/registry_controlled_domains/registry_controlled_domain.h"
|
| @@ -38,9 +39,6 @@ void StripTrailingDot(GURL* gurl) {
|
|
|
| namespace net {
|
|
|
| -//------------------------------------------------------------------------------
|
| -// static
|
| -
|
| // Adjust SDCH limits downwards for mobile.
|
| #if defined(OS_ANDROID) || defined(OS_IOS)
|
| // static
|
| @@ -58,7 +56,6 @@ bool SdchManager::g_sdch_enabled_ = true;
|
| // static
|
| bool SdchManager::g_secure_scheme_supported_ = true;
|
|
|
| -//------------------------------------------------------------------------------
|
| SdchManager::Dictionary::Dictionary(const std::string& dictionary_text,
|
| size_t offset,
|
| const std::string& client_hash,
|
| @@ -73,45 +70,22 @@ SdchManager::Dictionary::Dictionary(const std::string& dictionary_text,
|
| domain_(domain),
|
| path_(path),
|
| expiration_(expiration),
|
| - ports_(ports) {
|
| + ports_(ports),
|
| + clock_(new base::DefaultClock) {
|
| }
|
|
|
| -SdchManager::Dictionary::~Dictionary() {
|
| -}
|
| +SdchManager::Dictionary::Dictionary(const SdchManager::Dictionary& rhs)
|
| + : text_(rhs.text_),
|
| + client_hash_(rhs.client_hash_),
|
| + url_(rhs.url_),
|
| + domain_(rhs.domain_),
|
| + path_(rhs.path_),
|
| + expiration_(rhs.expiration_),
|
| + ports_(rhs.ports_),
|
| + clock_(new base::DefaultClock) {}
|
|
|
| -SdchProblemCode SdchManager::Dictionary::CanAdvertise(
|
| - const GURL& target_url) const {
|
| - /* The specific rules of when a dictionary should be advertised in an
|
| - Avail-Dictionary header are modeled after the rules for cookie scoping. The
|
| - terms "domain-match" and "pathmatch" are defined in RFC 2965 [6]. A
|
| - dictionary may be advertised in the Avail-Dictionaries header exactly when
|
| - all of the following are true:
|
| - 1. The server's effective host name domain-matches the Domain attribute of
|
| - the dictionary.
|
| - 2. If the dictionary has a Port attribute, the request port is one of the
|
| - ports listed in the Port attribute.
|
| - 3. The request URI path-matches the path header of the dictionary.
|
| - 4. The request is not an HTTPS request.
|
| - We can override (ignore) item (4) only when we have explicitly enabled
|
| - HTTPS support AND the dictionary acquisition scheme matches the target
|
| - url scheme.
|
| - */
|
| - if (!DomainMatch(target_url, domain_))
|
| - return SDCH_DICTIONARY_FOUND_HAS_WRONG_DOMAIN;
|
| - if (!ports_.empty() && 0 == ports_.count(target_url.EffectiveIntPort()))
|
| - return SDCH_DICTIONARY_FOUND_HAS_WRONG_PORT_LIST;
|
| - if (path_.size() && !PathMatch(target_url.path(), path_))
|
| - return SDCH_DICTIONARY_FOUND_HAS_WRONG_PATH;
|
| - if (!SdchManager::secure_scheme_supported() && target_url.SchemeIsSecure())
|
| - return SDCH_DICTIONARY_FOUND_HAS_WRONG_SCHEME;
|
| - if (target_url.SchemeIsSecure() != url_.SchemeIsSecure())
|
| - return SDCH_DICTIONARY_FOUND_HAS_WRONG_SCHEME;
|
| - if (base::Time::Now() > expiration_)
|
| - return SDCH_DICTIONARY_FOUND_EXPIRED;
|
| - return SDCH_OK;
|
| -}
|
| +SdchManager::Dictionary::~Dictionary() {}
|
|
|
| -//------------------------------------------------------------------------------
|
| // Security functions restricting loads and use of dictionaries.
|
|
|
| // static
|
| @@ -168,7 +142,7 @@ SdchProblemCode SdchManager::Dictionary::CanSet(const std::string& domain,
|
| }
|
|
|
| SdchProblemCode SdchManager::Dictionary::CanUse(
|
| - const GURL& referring_url) const {
|
| + const GURL& target_url) const {
|
| /*
|
| 1. The request URL's host name domain-matches the Domain attribute of the
|
| dictionary.
|
| @@ -180,24 +154,24 @@ SdchProblemCode SdchManager::Dictionary::CanUse(
|
| HTTPS support AND the dictionary acquisition scheme matches the target
|
| url scheme.
|
| */
|
| - if (!DomainMatch(referring_url, domain_))
|
| + if (!DomainMatch(target_url, domain_))
|
| return SDCH_DICTIONARY_FOUND_HAS_WRONG_DOMAIN;
|
|
|
| - if (!ports_.empty() && 0 == ports_.count(referring_url.EffectiveIntPort()))
|
| + if (!ports_.empty() && 0 == ports_.count(target_url.EffectiveIntPort()))
|
| return SDCH_DICTIONARY_FOUND_HAS_WRONG_PORT_LIST;
|
|
|
| - if (path_.size() && !PathMatch(referring_url.path(), path_))
|
| + if (path_.size() && !PathMatch(target_url.path(), path_))
|
| return SDCH_DICTIONARY_FOUND_HAS_WRONG_PATH;
|
|
|
| - if (!SdchManager::secure_scheme_supported() && referring_url.SchemeIsSecure())
|
| + if (!SdchManager::secure_scheme_supported() && target_url.SchemeIsSecure())
|
| return SDCH_DICTIONARY_FOUND_HAS_WRONG_SCHEME;
|
|
|
| - if (referring_url.SchemeIsSecure() != url_.SchemeIsSecure())
|
| + if (target_url.SchemeIsSecure() != url_.SchemeIsSecure())
|
| return SDCH_DICTIONARY_FOUND_HAS_WRONG_SCHEME;
|
|
|
| // TODO(jar): Remove overly restrictive failsafe test (added per security
|
| // review) when we have a need to be more general.
|
| - if (!referring_url.SchemeIsHTTPOrHTTPS())
|
| + if (!target_url.SchemeIsHTTPOrHTTPS())
|
| return SDCH_ATTEMPT_TO_DECODE_NON_HTTP_DATA;
|
|
|
| return SDCH_OK;
|
| @@ -228,7 +202,54 @@ bool SdchManager::Dictionary::DomainMatch(const GURL& gurl,
|
| return gurl.DomainIs(restriction.data(), restriction.size());
|
| }
|
|
|
| -//------------------------------------------------------------------------------
|
| +bool SdchManager::Dictionary::Expired() const {
|
| + return clock_->Now() > expiration_;
|
| +}
|
| +
|
| +void SdchManager::Dictionary::SetClockForTesting(
|
| + scoped_ptr<base::Clock> clock) {
|
| + clock_ = clock.Pass();
|
| +}
|
| +
|
| +SdchManager::DictionarySet::DictionarySet() {}
|
| +
|
| +SdchManager::DictionarySet::~DictionarySet() {}
|
| +
|
| +std::string SdchManager::DictionarySet::GetDictionaryClientHashList() const {
|
| + std::string result;
|
| + bool first = true;
|
| + for (const auto& entry: dictionaries_) {
|
| + if (!first)
|
| + result.append(",");
|
| +
|
| + result.append(entry.second->data.client_hash());
|
| + first = false;
|
| + }
|
| + return result;
|
| +}
|
| +
|
| +const SdchManager::Dictionary* SdchManager::DictionarySet::GetDictionary(
|
| + const std::string& hash) const {
|
| + auto it = dictionaries_.find(hash);
|
| + if (it == dictionaries_.end())
|
| + return NULL;
|
| +
|
| + return &it->second->data;
|
| +}
|
| +
|
| +bool SdchManager::DictionarySet::Empty() const {
|
| + return dictionaries_.empty();
|
| +}
|
| +
|
| +void SdchManager::DictionarySet::AddDictionary(
|
| + const std::string& server_hash,
|
| + const scoped_refptr<base::RefCountedData<SdchManager::Dictionary>>&
|
| + dictionary) {
|
| + DCHECK(dictionaries_.end() == dictionaries_.find(server_hash));
|
| +
|
| + dictionaries_[server_hash] = dictionary;
|
| +}
|
| +
|
| SdchManager::SdchManager() {
|
| DCHECK(thread_checker_.CalledOnValidThread());
|
| }
|
| @@ -236,7 +257,7 @@ SdchManager::SdchManager() {
|
| SdchManager::~SdchManager() {
|
| DCHECK(thread_checker_.CalledOnValidThread());
|
| while (!dictionaries_.empty()) {
|
| - DictionaryMap::iterator it = dictionaries_.begin();
|
| + auto it = dictionaries_.begin();
|
| dictionaries_.erase(it->first);
|
| }
|
| }
|
| @@ -246,7 +267,7 @@ void SdchManager::ClearData() {
|
| allow_latency_experiment_.clear();
|
|
|
| // Note that this may result in not having dictionaries we've advertised
|
| - // for incoming responses. The window is relatively small (as ClearData()
|
| + // for incoming responses. The window is relatively small (as ClearData()
|
| // is not expected to be called frequently), so we rely on meta-refresh
|
| // to handle this case.
|
| dictionaries_.clear();
|
| @@ -386,7 +407,7 @@ SdchProblemCode SdchManager::CanFetchDictionary(
|
| 3 The parent domain of the referrer URL host name is not a top level
|
| domain
|
| */
|
| - // Item (1) above implies item (2). Spec should be updated.
|
| + // Item (1) above implies item (2). Spec should be updated.
|
| // I take "host name match" to be "is identical to"
|
| if (referring_url.host() != dictionary_url.host() ||
|
| referring_url.scheme() != dictionary_url.scheme())
|
| @@ -403,51 +424,49 @@ SdchProblemCode SdchManager::CanFetchDictionary(
|
| return SDCH_OK;
|
| }
|
|
|
| -SdchProblemCode SdchManager::GetVcdiffDictionary(
|
| - const std::string& server_hash,
|
| - const GURL& referring_url,
|
| - scoped_refptr<Dictionary>* dictionary) {
|
| - DCHECK(thread_checker_.CalledOnValidThread());
|
| - *dictionary = NULL;
|
| - DictionaryMap::iterator it = dictionaries_.find(server_hash);
|
| - if (it == dictionaries_.end())
|
| - return SDCH_DICTIONARY_HASH_NOT_FOUND;
|
| -
|
| - scoped_refptr<Dictionary> matching_dictionary = it->second;
|
| -
|
| - SdchProblemCode rv = IsInSupportedDomain(referring_url);
|
| - if (rv != SDCH_OK)
|
| - return rv;
|
| -
|
| - rv = matching_dictionary->CanUse(referring_url);
|
| - if (rv == SDCH_OK)
|
| - *dictionary = matching_dictionary;
|
| - return rv;
|
| -}
|
| +scoped_ptr<SdchManager::DictionarySet>
|
| +SdchManager::GetDictionarySet(const GURL& target_url) {
|
| + if (IsInSupportedDomain(target_url) != SDCH_OK)
|
| + return NULL;
|
|
|
| -// TODO(jar): If we have evictions from the dictionaries_, then we need to
|
| -// change this interface to return a list of reference counted Dictionary
|
| -// instances that can be used if/when a server specifies one.
|
| -void SdchManager::GetAvailDictionaryList(const GURL& target_url,
|
| - std::string* list) {
|
| - DCHECK(thread_checker_.CalledOnValidThread());
|
| int count = 0;
|
| - for (DictionaryMap::iterator it = dictionaries_.begin();
|
| - it != dictionaries_.end(); ++it) {
|
| - SdchProblemCode rv = IsInSupportedDomain(target_url);
|
| - if (rv != SDCH_OK)
|
| + scoped_ptr<SdchManager::DictionarySet> result(new DictionarySet);
|
| + for (const auto& entry: dictionaries_) {
|
| + if (entry.second->data.CanUse(target_url) != SDCH_OK)
|
| continue;
|
| -
|
| - if (it->second->CanAdvertise(target_url) != SDCH_OK)
|
| + if (entry.second->data.Expired())
|
| continue;
|
| ++count;
|
| - if (!list->empty())
|
| - list->append(",");
|
| - list->append(it->second->client_hash());
|
| + result->AddDictionary(entry.first, entry.second);
|
| }
|
| - // Watch to see if we have corrupt or numerous dictionaries.
|
| - if (count > 0)
|
| - UMA_HISTOGRAM_COUNTS("Sdch3.Advertisement_Count", count);
|
| +
|
| + if (count == 0)
|
| + return NULL;
|
| +
|
| + UMA_HISTOGRAM_COUNTS("Sdch3.Advertisement_Count", count);
|
| +
|
| + return result.Pass();
|
| +}
|
| +
|
| +scoped_ptr<SdchManager::DictionarySet>
|
| +SdchManager::GetDictionarySetByHash(
|
| + const GURL& target_url,
|
| + const std::string& server_hash,
|
| + SdchProblemCode* problem_code) {
|
| + scoped_ptr<SdchManager::DictionarySet> result;
|
| +
|
| + *problem_code = SDCH_DICTIONARY_HASH_NOT_FOUND;
|
| + const auto& it = dictionaries_.find(server_hash);
|
| + if (it == dictionaries_.end())
|
| + return result;
|
| +
|
| + *problem_code = it->second->data.CanUse(target_url);
|
| + if (*problem_code != SDCH_OK)
|
| + return result;
|
| +
|
| + result.reset(new DictionarySet);
|
| + result->AddDictionary(it->first, it->second);
|
| + return result;
|
| }
|
|
|
| // static
|
| @@ -465,7 +484,6 @@ void SdchManager::GenerateHash(const std::string& dictionary_text,
|
| DCHECK_EQ(client_hash->length(), 8u);
|
| }
|
|
|
| -//------------------------------------------------------------------------------
|
| // Methods for supporting latency experiments.
|
|
|
| bool SdchManager::AllowLatencyExperiment(const GURL& url) const {
|
| @@ -575,8 +593,8 @@ SdchProblemCode SdchManager::AddSdchDictionary(
|
| return rv;
|
|
|
| // TODO(jar): Remove these hacks to preclude a DOS attack involving piles of
|
| - // useless dictionaries. We should probably have a cache eviction plan,
|
| - // instead of just blocking additions. For now, with the spec in flux, it
|
| + // useless dictionaries. We should probably have a cache eviction plan,
|
| + // instead of just blocking additions. For now, with the spec in flux, it
|
| // is probably not worth doing eviction handling.
|
| if (kMaxDictionarySize < dictionary_text.size())
|
| return SDCH_DICTIONARY_IS_TOO_LARGE;
|
| @@ -587,15 +605,22 @@ SdchProblemCode SdchManager::AddSdchDictionary(
|
| UMA_HISTOGRAM_COUNTS("Sdch3.Dictionary size loaded", dictionary_text.size());
|
| DVLOG(1) << "Loaded dictionary with client hash " << client_hash
|
| << " and server hash " << server_hash;
|
| - Dictionary* dictionary =
|
| - new Dictionary(dictionary_text, header_end + 2, client_hash,
|
| - dictionary_url_normalized, domain,
|
| - path, expiration, ports);
|
| - dictionaries_[server_hash] = dictionary;
|
| + Dictionary dictionary(dictionary_text, header_end + 2, client_hash,
|
| + dictionary_url_normalized, domain, path, expiration,
|
| + ports);
|
| + dictionaries_[server_hash] =
|
| + new base::RefCountedData<Dictionary>(dictionary);
|
| +
|
| return SDCH_OK;
|
| }
|
|
|
| // static
|
| +scoped_ptr<SdchManager::DictionarySet>
|
| +SdchManager::CreateEmptyDictionarySetForTesting() {
|
| + return scoped_ptr<DictionarySet>(new DictionarySet).Pass();
|
| +}
|
| +
|
| +// static
|
| void SdchManager::UrlSafeBase64Encode(const std::string& input,
|
| std::string* output) {
|
| // Since this is only done during a dictionary load, and hashes are only 8
|
| @@ -612,20 +637,20 @@ base::Value* SdchManager::SdchInfoToValue() const {
|
| value->SetBoolean("secure_scheme_support", secure_scheme_supported());
|
|
|
| base::ListValue* entry_list = new base::ListValue();
|
| - for (DictionaryMap::const_iterator it = dictionaries_.begin();
|
| - it != dictionaries_.end(); ++it) {
|
| + for (const auto& entry: dictionaries_) {
|
| base::DictionaryValue* entry_dict = new base::DictionaryValue();
|
| - entry_dict->SetString("url", it->second->url().spec());
|
| - entry_dict->SetString("client_hash", it->second->client_hash());
|
| - entry_dict->SetString("domain", it->second->domain());
|
| - entry_dict->SetString("path", it->second->path());
|
| + entry_dict->SetString("url", entry.second->data.url().spec());
|
| + entry_dict->SetString("client_hash", entry.second->data.client_hash());
|
| + entry_dict->SetString("domain", entry.second->data.domain());
|
| + entry_dict->SetString("path", entry.second->data.path());
|
| base::ListValue* port_list = new base::ListValue();
|
| - for (std::set<int>::const_iterator port_it = it->second->ports().begin();
|
| - port_it != it->second->ports().end(); ++port_it) {
|
| + for (std::set<int>::const_iterator port_it =
|
| + entry.second->data.ports().begin();
|
| + port_it != entry.second->data.ports().end(); ++port_it) {
|
| port_list->AppendInteger(*port_it);
|
| }
|
| entry_dict->Set("ports", port_list);
|
| - entry_dict->SetString("server_hash", it->first);
|
| + entry_dict->SetString("server_hash", entry.first);
|
| entry_list->Append(entry_dict);
|
| }
|
| value->Set("dictionaries", entry_list);
|
|
|