Index: net/base/sdch_manager.cc |
diff --git a/net/base/sdch_manager.cc b/net/base/sdch_manager.cc |
index 5aca923545241c7b8bf276c2fb0eb4ebe6c71560..18976c9e22e838204ce0f2af9c44db8c7fdf90f7 100644 |
--- a/net/base/sdch_manager.cc |
+++ b/net/base/sdch_manager.cc |
@@ -26,41 +26,176 @@ const size_t SdchManager::kMaxDictionaryCount = 20; |
// static |
SdchManager* SdchManager::global_; |
-// static |
-SdchManager* SdchManager::Global() { |
- return global_; |
+//------------------------------------------------------------------------------ |
+SdchManager::Dictionary::Dictionary(const std::string& dictionary_text, |
+ size_t offset, const std::string& client_hash, const GURL& gurl, |
+ const std::string& domain, const std::string& path, const Time& expiration, |
+ const std::set<int> ports) |
+ : text_(dictionary_text, offset), |
+ client_hash_(client_hash), |
+ url_(gurl), |
+ domain_(domain), |
+ path_(path), |
+ expiration_(expiration), |
+ ports_(ports) { |
} |
-// static |
-void SdchManager::SdchErrorRecovery(ProblemCodes problem) { |
- UMA_HISTOGRAM_ENUMERATION("Sdch3.ProblemCodes_4", problem, MAX_PROBLEM_CODE); |
+SdchManager::Dictionary::~Dictionary() { |
} |
-// static |
-void SdchManager::ClearBlacklistings() { |
- Global()->blacklisted_domains_.clear(); |
- Global()->exponential_blacklist_count.clear(); |
+bool SdchManager::Dictionary::CanAdvertise(const GURL& target_url) { |
+ if (!SdchManager::Global()->IsInSupportedDomain(target_url)) |
+ return false; |
+ /* 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. |
+ */ |
+ if (!DomainMatch(target_url, domain_)) |
+ return false; |
+ if (!ports_.empty() && 0 == ports_.count(target_url.EffectiveIntPort())) |
+ return false; |
+ if (path_.size() && !PathMatch(target_url.path(), path_)) |
+ return false; |
+ if (target_url.SchemeIsSecure()) |
+ return false; |
+ if (Time::Now() > expiration_) |
+ return false; |
+ return true; |
} |
+//------------------------------------------------------------------------------ |
+// Security functions restricting loads and use of dictionaries. |
+ |
// static |
-void SdchManager::ClearDomainBlacklisting(const std::string& domain) { |
- Global()->blacklisted_domains_.erase(StringToLowerASCII(domain)); |
+bool SdchManager::Dictionary::CanSet(const std::string& domain, |
+ const std::string& path, |
+ const std::set<int> ports, |
+ const GURL& dictionary_url) { |
+ if (!SdchManager::Global()->IsInSupportedDomain(dictionary_url)) |
+ return false; |
+ /* |
+ A dictionary is invalid and must not be stored if any of the following are |
+ true: |
+ 1. The dictionary has no Domain attribute. |
+ 2. The effective host name that derives from the referer URL host name does |
+ not domain-match the Domain attribute. |
+ 3. The Domain attribute is a top level domain. |
+ 4. The referer URL host is a host domain name (not IP address) and has the |
+ form HD, where D is the value of the Domain attribute, and H is a string |
+ that contains one or more dots. |
+ 5. If the dictionary has a Port attribute and the referer URL's port was not |
+ in the list. |
+ */ |
+ |
+ // TODO(jar): Redirects in dictionary fetches might plausibly be problematic, |
+ // and hence the conservative approach is to not allow any redirects (if there |
+ // were any... then don't allow the dictionary to be set). |
+ |
+ if (domain.empty()) { |
+ SdchErrorRecovery(DICTIONARY_MISSING_DOMAIN_SPECIFIER); |
+ return false; // Domain is required. |
+ } |
+ if (net::RegistryControlledDomainService::GetDomainAndRegistry(domain).size() |
+ == 0) { |
+ SdchErrorRecovery(DICTIONARY_SPECIFIES_TOP_LEVEL_DOMAIN); |
+ return false; // domain was a TLD. |
+ } |
+ if (!Dictionary::DomainMatch(dictionary_url, domain)) { |
+ SdchErrorRecovery(DICTIONARY_DOMAIN_NOT_MATCHING_SOURCE_URL); |
+ return false; |
+ } |
+ |
+ std::string referrer_url_host = dictionary_url.host(); |
+ size_t postfix_domain_index = referrer_url_host.rfind(domain); |
+ // See if it is indeed a postfix, or just an internal string. |
+ if (referrer_url_host.size() == postfix_domain_index + domain.size()) { |
+ // It is a postfix... so check to see if there's a dot in the prefix. |
+ size_t end_of_host_index = referrer_url_host.find_first_of('.'); |
+ if (referrer_url_host.npos != end_of_host_index && |
+ end_of_host_index < postfix_domain_index) { |
+ SdchErrorRecovery(DICTIONARY_REFERER_URL_HAS_DOT_IN_PREFIX); |
+ return false; |
+ } |
+ } |
+ |
+ if (!ports.empty() |
+ && 0 == ports.count(dictionary_url.EffectiveIntPort())) { |
+ SdchErrorRecovery(DICTIONARY_PORT_NOT_MATCHING_SOURCE_URL); |
+ return false; |
+ } |
+ return true; |
} |
// static |
-int SdchManager::BlackListDomainCount(const std::string& domain) { |
- if (Global()->blacklisted_domains_.end() == |
- Global()->blacklisted_domains_.find(domain)) |
- return 0; |
- return Global()->blacklisted_domains_[StringToLowerASCII(domain)]; |
+bool SdchManager::Dictionary::CanUse(const GURL& referring_url) { |
+ if (!SdchManager::Global()->IsInSupportedDomain(referring_url)) |
+ return false; |
+ /* |
+ 1. The request URL's 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 URL path-matches the path attribute of the dictionary. |
+ 4. The request is not an HTTPS request. |
+*/ |
+ if (!DomainMatch(referring_url, domain_)) { |
+ SdchErrorRecovery(DICTIONARY_FOUND_HAS_WRONG_DOMAIN); |
+ return false; |
+ } |
+ if (!ports_.empty() |
+ && 0 == ports_.count(referring_url.EffectiveIntPort())) { |
+ SdchErrorRecovery(DICTIONARY_FOUND_HAS_WRONG_PORT_LIST); |
+ return false; |
+ } |
+ if (path_.size() && !PathMatch(referring_url.path(), path_)) { |
+ SdchErrorRecovery(DICTIONARY_FOUND_HAS_WRONG_PATH); |
+ return false; |
+ } |
+ if (referring_url.SchemeIsSecure()) { |
+ SdchErrorRecovery(DICTIONARY_FOUND_HAS_WRONG_SCHEME); |
+ return false; |
+ } |
+ |
+ // TODO(jar): Remove overly restrictive failsafe test (added per security |
+ // review) when we have a need to be more general. |
+ if (!referring_url.SchemeIs("http")) { |
+ SdchErrorRecovery(ATTEMPT_TO_DECODE_NON_HTTP_DATA); |
+ return false; |
+ } |
+ |
+ return true; |
+} |
+ |
+bool SdchManager::Dictionary::PathMatch(const std::string& path, |
+ const std::string& restriction) { |
+ /* Must be either: |
+ 1. P2 is equal to P1 |
+ 2. P2 is a prefix of P1 and either the final character in P2 is "/" or the |
+ character following P2 in P1 is "/". |
+ */ |
+ if (path == restriction) |
+ return true; |
+ size_t prefix_length = restriction.size(); |
+ if (prefix_length > path.size()) |
+ return false; // Can't be a prefix. |
+ if (0 != path.compare(0, prefix_length, restriction)) |
+ return false; |
+ return restriction[prefix_length - 1] == '/' || path[prefix_length] == '/'; |
} |
// static |
-int SdchManager::BlacklistDomainExponential(const std::string& domain) { |
- if (Global()->exponential_blacklist_count.end() == |
- Global()->exponential_blacklist_count.find(domain)) |
- return 0; |
- return Global()->exponential_blacklist_count[StringToLowerASCII(domain)]; |
+bool SdchManager::Dictionary::DomainMatch(const GURL& gurl, |
+ const std::string& restriction) { |
+ // TODO(jar): This is not precisely a domain match definition. |
+ return gurl.DomainIs(restriction.data(), restriction.size()); |
} |
//------------------------------------------------------------------------------ |
@@ -87,6 +222,22 @@ void SdchManager::Shutdown() { |
} |
// static |
+SdchManager* SdchManager::Global() { |
+ return global_; |
+} |
+ |
+// static |
+void SdchManager::SdchErrorRecovery(ProblemCodes problem) { |
+ UMA_HISTOGRAM_ENUMERATION("Sdch3.ProblemCodes_4", problem, MAX_PROBLEM_CODE); |
+} |
+ |
+void SdchManager::EnableSdchSupport(const std::string& domain) { |
+ // We presume that there is a SDCH manager instance. |
+ global_->supported_domain_ = domain; |
+ global_->sdch_enabled_ = true; |
+} |
+ |
+// static |
void SdchManager::BlacklistDomain(const GURL& url) { |
if (!global_ ) |
return; |
@@ -117,10 +268,31 @@ void SdchManager::BlacklistDomainForever(const GURL& url) { |
global_->blacklisted_domains_[domain] = INT_MAX; |
} |
-void SdchManager::EnableSdchSupport(const std::string& domain) { |
- // We presume that there is a SDCH manager instance. |
- global_->supported_domain_ = domain; |
- global_->sdch_enabled_ = true; |
+// static |
+void SdchManager::ClearBlacklistings() { |
+ Global()->blacklisted_domains_.clear(); |
+ Global()->exponential_blacklist_count.clear(); |
+} |
+ |
+// static |
+void SdchManager::ClearDomainBlacklisting(const std::string& domain) { |
+ Global()->blacklisted_domains_.erase(StringToLowerASCII(domain)); |
+} |
+ |
+// static |
+int SdchManager::BlackListDomainCount(const std::string& domain) { |
+ if (Global()->blacklisted_domains_.end() == |
+ Global()->blacklisted_domains_.find(domain)) |
+ return 0; |
+ return Global()->blacklisted_domains_[StringToLowerASCII(domain)]; |
+} |
+ |
+// static |
+int SdchManager::BlacklistDomainExponential(const std::string& domain) { |
+ if (Global()->exponential_blacklist_count.end() == |
+ Global()->exponential_blacklist_count.find(domain)) |
+ return 0; |
+ return Global()->exponential_blacklist_count[StringToLowerASCII(domain)]; |
} |
bool SdchManager::IsInSupportedDomain(const GURL& url) { |
@@ -147,6 +319,13 @@ bool SdchManager::IsInSupportedDomain(const GURL& url) { |
return false; |
} |
+void SdchManager::FetchDictionary(const GURL& request_url, |
+ const GURL& dictionary_url) { |
+ if (SdchManager::Global()->CanFetchDictionary(request_url, dictionary_url) && |
+ fetcher_.get()) |
+ fetcher_->Schedule(dictionary_url); |
+} |
+ |
bool SdchManager::CanFetchDictionary(const GURL& referring_url, |
const GURL& dictionary_url) const { |
/* The user agent may retrieve a dictionary from the dictionary URL if all of |
@@ -179,13 +358,6 @@ bool SdchManager::CanFetchDictionary(const GURL& referring_url, |
return true; |
} |
-void SdchManager::FetchDictionary(const GURL& request_url, |
- const GURL& dictionary_url) { |
- if (SdchManager::Global()->CanFetchDictionary(request_url, dictionary_url) && |
- fetcher_.get()) |
- fetcher_->Schedule(dictionary_url); |
-} |
- |
bool SdchManager::AddSdchDictionary(const std::string& dictionary_text, |
const GURL& dictionary_url) { |
std::string client_hash; |
@@ -317,22 +489,6 @@ void SdchManager::GetAvailDictionaryList(const GURL& target_url, |
UMA_HISTOGRAM_COUNTS("Sdch3.Advertisement_Count", count); |
} |
-SdchManager::Dictionary::Dictionary(const std::string& dictionary_text, |
- size_t offset, const std::string& client_hash, const GURL& gurl, |
- const std::string& domain, const std::string& path, const Time& expiration, |
- const std::set<int> ports) |
- : text_(dictionary_text, offset), |
- client_hash_(client_hash), |
- url_(gurl), |
- domain_(domain), |
- path_(path), |
- expiration_(expiration), |
- ports_(ports) { |
-} |
- |
-SdchManager::Dictionary::~Dictionary() { |
-} |
- |
// static |
void SdchManager::GenerateHash(const std::string& dictionary_text, |
std::string* client_hash, std::string* server_hash) { |
@@ -348,181 +504,6 @@ void SdchManager::GenerateHash(const std::string& dictionary_text, |
DCHECK_EQ(client_hash->length(), 8u); |
} |
-// 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 |
- // characters, we just do the simple fixup, rather than rewriting the encoder. |
- base::Base64Encode(input, output); |
- for (size_t i = 0; i < output->size(); ++i) { |
- switch (output->data()[i]) { |
- case '+': |
- (*output)[i] = '-'; |
- continue; |
- case '/': |
- (*output)[i] = '_'; |
- continue; |
- default: |
- continue; |
- } |
- } |
-} |
- |
-//------------------------------------------------------------------------------ |
-// Security functions restricting loads and use of dictionaries. |
- |
-// static |
-bool SdchManager::Dictionary::CanSet(const std::string& domain, |
- const std::string& path, |
- const std::set<int> ports, |
- const GURL& dictionary_url) { |
- if (!SdchManager::Global()->IsInSupportedDomain(dictionary_url)) |
- return false; |
- /* |
- A dictionary is invalid and must not be stored if any of the following are |
- true: |
- 1. The dictionary has no Domain attribute. |
- 2. The effective host name that derives from the referer URL host name does |
- not domain-match the Domain attribute. |
- 3. The Domain attribute is a top level domain. |
- 4. The referer URL host is a host domain name (not IP address) and has the |
- form HD, where D is the value of the Domain attribute, and H is a string |
- that contains one or more dots. |
- 5. If the dictionary has a Port attribute and the referer URL's port was not |
- in the list. |
- */ |
- |
- // TODO(jar): Redirects in dictionary fetches might plausibly be problematic, |
- // and hence the conservative approach is to not allow any redirects (if there |
- // were any... then don't allow the dictionary to be set). |
- |
- if (domain.empty()) { |
- SdchErrorRecovery(DICTIONARY_MISSING_DOMAIN_SPECIFIER); |
- return false; // Domain is required. |
- } |
- if (net::RegistryControlledDomainService::GetDomainAndRegistry(domain).size() |
- == 0) { |
- SdchErrorRecovery(DICTIONARY_SPECIFIES_TOP_LEVEL_DOMAIN); |
- return false; // domain was a TLD. |
- } |
- if (!Dictionary::DomainMatch(dictionary_url, domain)) { |
- SdchErrorRecovery(DICTIONARY_DOMAIN_NOT_MATCHING_SOURCE_URL); |
- return false; |
- } |
- |
- std::string referrer_url_host = dictionary_url.host(); |
- size_t postfix_domain_index = referrer_url_host.rfind(domain); |
- // See if it is indeed a postfix, or just an internal string. |
- if (referrer_url_host.size() == postfix_domain_index + domain.size()) { |
- // It is a postfix... so check to see if there's a dot in the prefix. |
- size_t end_of_host_index = referrer_url_host.find_first_of('.'); |
- if (referrer_url_host.npos != end_of_host_index && |
- end_of_host_index < postfix_domain_index) { |
- SdchErrorRecovery(DICTIONARY_REFERER_URL_HAS_DOT_IN_PREFIX); |
- return false; |
- } |
- } |
- |
- if (!ports.empty() |
- && 0 == ports.count(dictionary_url.EffectiveIntPort())) { |
- SdchErrorRecovery(DICTIONARY_PORT_NOT_MATCHING_SOURCE_URL); |
- return false; |
- } |
- return true; |
-} |
- |
-// static |
-bool SdchManager::Dictionary::CanUse(const GURL& referring_url) { |
- if (!SdchManager::Global()->IsInSupportedDomain(referring_url)) |
- return false; |
- /* |
- 1. The request URL's 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 URL path-matches the path attribute of the dictionary. |
- 4. The request is not an HTTPS request. |
-*/ |
- if (!DomainMatch(referring_url, domain_)) { |
- SdchErrorRecovery(DICTIONARY_FOUND_HAS_WRONG_DOMAIN); |
- return false; |
- } |
- if (!ports_.empty() |
- && 0 == ports_.count(referring_url.EffectiveIntPort())) { |
- SdchErrorRecovery(DICTIONARY_FOUND_HAS_WRONG_PORT_LIST); |
- return false; |
- } |
- if (path_.size() && !PathMatch(referring_url.path(), path_)) { |
- SdchErrorRecovery(DICTIONARY_FOUND_HAS_WRONG_PATH); |
- return false; |
- } |
- if (referring_url.SchemeIsSecure()) { |
- SdchErrorRecovery(DICTIONARY_FOUND_HAS_WRONG_SCHEME); |
- return false; |
- } |
- |
- // TODO(jar): Remove overly restrictive failsafe test (added per security |
- // review) when we have a need to be more general. |
- if (!referring_url.SchemeIs("http")) { |
- SdchErrorRecovery(ATTEMPT_TO_DECODE_NON_HTTP_DATA); |
- return false; |
- } |
- |
- return true; |
-} |
- |
-bool SdchManager::Dictionary::CanAdvertise(const GURL& target_url) { |
- if (!SdchManager::Global()->IsInSupportedDomain(target_url)) |
- return false; |
- /* 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. |
- */ |
- if (!DomainMatch(target_url, domain_)) |
- return false; |
- if (!ports_.empty() && 0 == ports_.count(target_url.EffectiveIntPort())) |
- return false; |
- if (path_.size() && !PathMatch(target_url.path(), path_)) |
- return false; |
- if (target_url.SchemeIsSecure()) |
- return false; |
- if (Time::Now() > expiration_) |
- return false; |
- return true; |
-} |
- |
-bool SdchManager::Dictionary::PathMatch(const std::string& path, |
- const std::string& restriction) { |
- /* Must be either: |
- 1. P2 is equal to P1 |
- 2. P2 is a prefix of P1 and either the final character in P2 is "/" or the |
- character following P2 in P1 is "/". |
- */ |
- if (path == restriction) |
- return true; |
- size_t prefix_length = restriction.size(); |
- if (prefix_length > path.size()) |
- return false; // Can't be a prefix. |
- if (0 != path.compare(0, prefix_length, restriction)) |
- return false; |
- return restriction[prefix_length - 1] == '/' || path[prefix_length] == '/'; |
-} |
- |
-// static |
-bool SdchManager::Dictionary::DomainMatch(const GURL& gurl, |
- const std::string& restriction) { |
- // TODO(jar): This is not precisely a domain match definition. |
- return gurl.DomainIs(restriction.data(), restriction.size()); |
-} |
- |
//------------------------------------------------------------------------------ |
// Methods for supporting latency experiments. |
@@ -542,3 +523,23 @@ void SdchManager::SetAllowLatencyExperiment(const GURL& url, bool enable) { |
SdchErrorRecovery(LATENCY_TEST_DISALLOWED); |
allow_latency_experiment_.erase(it); |
} |
+ |
+// 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 |
+ // characters, we just do the simple fixup, rather than rewriting the encoder. |
+ base::Base64Encode(input, output); |
+ for (size_t i = 0; i < output->size(); ++i) { |
+ switch (output->data()[i]) { |
+ case '+': |
+ (*output)[i] = '-'; |
+ continue; |
+ case '/': |
+ (*output)[i] = '_'; |
+ continue; |
+ default: |
+ continue; |
+ } |
+ } |
+} |