[email protected] | be28b5f4 | 2012-07-20 11:31:25 | [diff] [blame] | 1 | // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
[email protected] | 6088942 | 2008-09-23 01:18:16 | [diff] [blame] | 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
[email protected] | e83326f | 2010-07-31 17:29:25 | [diff] [blame] | 5 | #include "net/base/sdch_manager.h" |
| 6 | |
[email protected] | 978df34 | 2009-11-24 06:21:53 | [diff] [blame] | 7 | #include "base/base64.h" |
[email protected] | 6088942 | 2008-09-23 01:18:16 | [diff] [blame] | 8 | #include "base/logging.h" |
[email protected] | 835d7c8 | 2010-10-14 04:38:38 | [diff] [blame] | 9 | #include "base/metrics/histogram.h" |
[email protected] | 4b35521 | 2013-06-11 10:35:19 | [diff] [blame] | 10 | #include "base/strings/string_number_conversions.h" |
| 11 | #include "base/strings/string_util.h" |
rdsmith | 81f60756 | 2014-11-21 18:35:16 | [diff] [blame] | 12 | #include "base/time/default_clock.h" |
baranovich | c5e3865 | 2014-11-14 03:08:15 | [diff] [blame] | 13 | #include "base/values.h" |
[email protected] | 4b559b4d | 2011-04-14 17:37:14 | [diff] [blame] | 14 | #include "crypto/sha2.h" |
[email protected] | be28b5f4 | 2012-07-20 11:31:25 | [diff] [blame] | 15 | #include "net/base/registry_controlled_domains/registry_controlled_domain.h" |
rdsmith | 47c133b | 2014-11-06 19:02:30 | [diff] [blame] | 16 | #include "net/base/sdch_observer.h" |
[email protected] | 6088942 | 2008-09-23 01:18:16 | [diff] [blame] | 17 | #include "net/url_request/url_request_http_job.h" |
| 18 | |
rdsmith | 14fd659 | 2014-09-23 21:13:10 | [diff] [blame] | 19 | namespace { |
| 20 | |
| 21 | void StripTrailingDot(GURL* gurl) { |
| 22 | std::string host(gurl->host()); |
| 23 | |
| 24 | if (host.empty()) |
| 25 | return; |
| 26 | |
| 27 | if (*host.rbegin() != '.') |
| 28 | return; |
| 29 | |
| 30 | host.resize(host.size() - 1); |
| 31 | |
| 32 | GURL::Replacements replacements; |
| 33 | replacements.SetHostStr(host); |
| 34 | *gurl = gurl->ReplaceComponents(replacements); |
| 35 | return; |
| 36 | } |
| 37 | |
| 38 | } // namespace |
| 39 | |
[email protected] | ba5734e | 2011-02-01 03:42:11 | [diff] [blame] | 40 | namespace net { |
[email protected] | 6088942 | 2008-09-23 01:18:16 | [diff] [blame] | 41 | |
rdsmith | c4add97e | 2014-12-17 17:24:31 | [diff] [blame] | 42 | // Workaround for http://crbug.com/437794; remove when fixed. |
| 43 | #if defined(OS_IOS) |
| 44 | // static |
| 45 | bool SdchManager::g_sdch_enabled_ = false; |
| 46 | #else |
[email protected] | 075bc8cb | 2008-11-18 20:22:52 | [diff] [blame] | 47 | // static |
rdsmith | 24322bca | 2014-10-29 15:50:20 | [diff] [blame] | 48 | bool SdchManager::g_sdch_enabled_ = true; |
rdsmith | c4add97e | 2014-12-17 17:24:31 | [diff] [blame] | 49 | #endif |
[email protected] | ef99310 | 2014-01-10 19:29:37 | [diff] [blame] | 50 | |
rdsmith | 24322bca | 2014-10-29 15:50:20 | [diff] [blame] | 51 | // static |
| 52 | bool SdchManager::g_secure_scheme_supported_ = true; |
| 53 | |
[email protected] | 4b3c95dd | 2011-01-07 23:02:11 | [diff] [blame] | 54 | SdchManager::Dictionary::Dictionary(const std::string& dictionary_text, |
[email protected] | ba5734e | 2011-02-01 03:42:11 | [diff] [blame] | 55 | size_t offset, |
| 56 | const std::string& client_hash, |
rdsmith | a8a4e3c8 | 2015-01-08 20:18:17 | [diff] [blame] | 57 | const std::string& server_hash, |
[email protected] | ba5734e | 2011-02-01 03:42:11 | [diff] [blame] | 58 | const GURL& gurl, |
| 59 | const std::string& domain, |
| 60 | const std::string& path, |
| 61 | const base::Time& expiration, |
| 62 | const std::set<int>& ports) |
| 63 | : text_(dictionary_text, offset), |
| 64 | client_hash_(client_hash), |
rdsmith | a8a4e3c8 | 2015-01-08 20:18:17 | [diff] [blame] | 65 | server_hash_(server_hash), |
[email protected] | ba5734e | 2011-02-01 03:42:11 | [diff] [blame] | 66 | url_(gurl), |
| 67 | domain_(domain), |
| 68 | path_(path), |
| 69 | expiration_(expiration), |
rdsmith | 81f60756 | 2014-11-21 18:35:16 | [diff] [blame] | 70 | ports_(ports), |
| 71 | clock_(new base::DefaultClock) { |
[email protected] | 4b3c95dd | 2011-01-07 23:02:11 | [diff] [blame] | 72 | } |
| 73 | |
rdsmith | 81f60756 | 2014-11-21 18:35:16 | [diff] [blame] | 74 | SdchManager::Dictionary::Dictionary(const SdchManager::Dictionary& rhs) |
| 75 | : text_(rhs.text_), |
| 76 | client_hash_(rhs.client_hash_), |
rdsmith | a8a4e3c8 | 2015-01-08 20:18:17 | [diff] [blame] | 77 | server_hash_(rhs.server_hash_), |
rdsmith | 81f60756 | 2014-11-21 18:35:16 | [diff] [blame] | 78 | url_(rhs.url_), |
| 79 | domain_(rhs.domain_), |
| 80 | path_(rhs.path_), |
| 81 | expiration_(rhs.expiration_), |
| 82 | ports_(rhs.ports_), |
rdsmith | a8a4e3c8 | 2015-01-08 20:18:17 | [diff] [blame] | 83 | clock_(new base::DefaultClock) { |
| 84 | } |
[email protected] | 4b3c95dd | 2011-01-07 23:02:11 | [diff] [blame] | 85 | |
rdsmith | 81f60756 | 2014-11-21 18:35:16 | [diff] [blame] | 86 | SdchManager::Dictionary::~Dictionary() {} |
[email protected] | 4b3c95dd | 2011-01-07 23:02:11 | [diff] [blame] | 87 | |
[email protected] | 4b3c95dd | 2011-01-07 23:02:11 | [diff] [blame] | 88 | // Security functions restricting loads and use of dictionaries. |
| 89 | |
[email protected] | 6088942 | 2008-09-23 01:18:16 | [diff] [blame] | 90 | // static |
baranovich | c5e3865 | 2014-11-14 03:08:15 | [diff] [blame] | 91 | SdchProblemCode SdchManager::Dictionary::CanSet(const std::string& domain, |
| 92 | const std::string& path, |
| 93 | const std::set<int>& ports, |
| 94 | const GURL& dictionary_url) { |
[email protected] | 4b3c95dd | 2011-01-07 23:02:11 | [diff] [blame] | 95 | /* |
| 96 | A dictionary is invalid and must not be stored if any of the following are |
| 97 | true: |
| 98 | 1. The dictionary has no Domain attribute. |
| 99 | 2. The effective host name that derives from the referer URL host name does |
| 100 | not domain-match the Domain attribute. |
| 101 | 3. The Domain attribute is a top level domain. |
| 102 | 4. The referer URL host is a host domain name (not IP address) and has the |
| 103 | form HD, where D is the value of the Domain attribute, and H is a string |
| 104 | that contains one or more dots. |
| 105 | 5. If the dictionary has a Port attribute and the referer URL's port was not |
| 106 | in the list. |
| 107 | */ |
| 108 | |
| 109 | // TODO(jar): Redirects in dictionary fetches might plausibly be problematic, |
| 110 | // and hence the conservative approach is to not allow any redirects (if there |
| 111 | // were any... then don't allow the dictionary to be set). |
| 112 | |
baranovich | c5e3865 | 2014-11-14 03:08:15 | [diff] [blame] | 113 | if (domain.empty()) |
| 114 | return SDCH_DICTIONARY_MISSING_DOMAIN_SPECIFIER; // Domain is required. |
| 115 | |
tzik | 2f98de30 | 2014-11-04 06:38:19 | [diff] [blame] | 116 | if (registry_controlled_domains::GetDomainAndRegistry( |
baranovich | c5e3865 | 2014-11-14 03:08:15 | [diff] [blame] | 117 | domain, registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES) |
| 118 | .empty()) { |
| 119 | return SDCH_DICTIONARY_SPECIFIES_TOP_LEVEL_DOMAIN; // domain was a TLD. |
tzik | 2f98de30 | 2014-11-04 06:38:19 | [diff] [blame] | 120 | } |
baranovich | c5e3865 | 2014-11-14 03:08:15 | [diff] [blame] | 121 | |
| 122 | if (!Dictionary::DomainMatch(dictionary_url, domain)) |
| 123 | return SDCH_DICTIONARY_DOMAIN_NOT_MATCHING_SOURCE_URL; |
[email protected] | 4b3c95dd | 2011-01-07 23:02:11 | [diff] [blame] | 124 | |
| 125 | std::string referrer_url_host = dictionary_url.host(); |
| 126 | size_t postfix_domain_index = referrer_url_host.rfind(domain); |
| 127 | // See if it is indeed a postfix, or just an internal string. |
| 128 | if (referrer_url_host.size() == postfix_domain_index + domain.size()) { |
| 129 | // It is a postfix... so check to see if there's a dot in the prefix. |
| 130 | size_t end_of_host_index = referrer_url_host.find_first_of('.'); |
baranovich | c5e3865 | 2014-11-14 03:08:15 | [diff] [blame] | 131 | if (referrer_url_host.npos != end_of_host_index && |
[email protected] | 4b3c95dd | 2011-01-07 23:02:11 | [diff] [blame] | 132 | end_of_host_index < postfix_domain_index) { |
baranovich | c5e3865 | 2014-11-14 03:08:15 | [diff] [blame] | 133 | return SDCH_DICTIONARY_REFERER_URL_HAS_DOT_IN_PREFIX; |
[email protected] | 4b3c95dd | 2011-01-07 23:02:11 | [diff] [blame] | 134 | } |
| 135 | } |
| 136 | |
baranovich | c5e3865 | 2014-11-14 03:08:15 | [diff] [blame] | 137 | if (!ports.empty() && 0 == ports.count(dictionary_url.EffectiveIntPort())) |
| 138 | return SDCH_DICTIONARY_PORT_NOT_MATCHING_SOURCE_URL; |
| 139 | |
| 140 | return SDCH_OK; |
[email protected] | 6088942 | 2008-09-23 01:18:16 | [diff] [blame] | 141 | } |
| 142 | |
baranovich | c5e3865 | 2014-11-14 03:08:15 | [diff] [blame] | 143 | SdchProblemCode SdchManager::Dictionary::CanUse( |
rdsmith | 81f60756 | 2014-11-21 18:35:16 | [diff] [blame] | 144 | const GURL& target_url) const { |
[email protected] | 4b3c95dd | 2011-01-07 23:02:11 | [diff] [blame] | 145 | /* |
| 146 | 1. The request URL's host name domain-matches the Domain attribute of the |
| 147 | dictionary. |
| 148 | 2. If the dictionary has a Port attribute, the request port is one of the |
| 149 | ports listed in the Port attribute. |
| 150 | 3. The request URL path-matches the path attribute of the dictionary. |
| 151 | 4. The request is not an HTTPS request. |
[email protected] | ef99310 | 2014-01-10 19:29:37 | [diff] [blame] | 152 | We can override (ignore) item (4) only when we have explicitly enabled |
[email protected] | ed2f7cc | 2014-07-08 02:35:49 | [diff] [blame] | 153 | HTTPS support AND the dictionary acquisition scheme matches the target |
| 154 | url scheme. |
| 155 | */ |
rdsmith | 81f60756 | 2014-11-21 18:35:16 | [diff] [blame] | 156 | if (!DomainMatch(target_url, domain_)) |
baranovich | c5e3865 | 2014-11-14 03:08:15 | [diff] [blame] | 157 | return SDCH_DICTIONARY_FOUND_HAS_WRONG_DOMAIN; |
| 158 | |
rdsmith | 81f60756 | 2014-11-21 18:35:16 | [diff] [blame] | 159 | if (!ports_.empty() && 0 == ports_.count(target_url.EffectiveIntPort())) |
baranovich | c5e3865 | 2014-11-14 03:08:15 | [diff] [blame] | 160 | return SDCH_DICTIONARY_FOUND_HAS_WRONG_PORT_LIST; |
| 161 | |
rdsmith | 81f60756 | 2014-11-21 18:35:16 | [diff] [blame] | 162 | if (path_.size() && !PathMatch(target_url.path(), path_)) |
baranovich | c5e3865 | 2014-11-14 03:08:15 | [diff] [blame] | 163 | return SDCH_DICTIONARY_FOUND_HAS_WRONG_PATH; |
| 164 | |
rdsmith | 81f60756 | 2014-11-21 18:35:16 | [diff] [blame] | 165 | if (!SdchManager::secure_scheme_supported() && target_url.SchemeIsSecure()) |
baranovich | c5e3865 | 2014-11-14 03:08:15 | [diff] [blame] | 166 | return SDCH_DICTIONARY_FOUND_HAS_WRONG_SCHEME; |
| 167 | |
rdsmith | 81f60756 | 2014-11-21 18:35:16 | [diff] [blame] | 168 | if (target_url.SchemeIsSecure() != url_.SchemeIsSecure()) |
baranovich | c5e3865 | 2014-11-14 03:08:15 | [diff] [blame] | 169 | return SDCH_DICTIONARY_FOUND_HAS_WRONG_SCHEME; |
[email protected] | 4b3c95dd | 2011-01-07 23:02:11 | [diff] [blame] | 170 | |
| 171 | // TODO(jar): Remove overly restrictive failsafe test (added per security |
| 172 | // review) when we have a need to be more general. |
rdsmith | 81f60756 | 2014-11-21 18:35:16 | [diff] [blame] | 173 | if (!target_url.SchemeIsHTTPOrHTTPS()) |
baranovich | c5e3865 | 2014-11-14 03:08:15 | [diff] [blame] | 174 | return SDCH_ATTEMPT_TO_DECODE_NON_HTTP_DATA; |
[email protected] | 4b3c95dd | 2011-01-07 23:02:11 | [diff] [blame] | 175 | |
baranovich | c5e3865 | 2014-11-14 03:08:15 | [diff] [blame] | 176 | return SDCH_OK; |
[email protected] | 4b3c95dd | 2011-01-07 23:02:11 | [diff] [blame] | 177 | } |
| 178 | |
baranovich | c5e3865 | 2014-11-14 03:08:15 | [diff] [blame] | 179 | // static |
[email protected] | 4b3c95dd | 2011-01-07 23:02:11 | [diff] [blame] | 180 | bool SdchManager::Dictionary::PathMatch(const std::string& path, |
| 181 | const std::string& restriction) { |
| 182 | /* Must be either: |
| 183 | 1. P2 is equal to P1 |
| 184 | 2. P2 is a prefix of P1 and either the final character in P2 is "/" or the |
| 185 | character following P2 in P1 is "/". |
| 186 | */ |
| 187 | if (path == restriction) |
| 188 | return true; |
| 189 | size_t prefix_length = restriction.size(); |
| 190 | if (prefix_length > path.size()) |
| 191 | return false; // Can't be a prefix. |
| 192 | if (0 != path.compare(0, prefix_length, restriction)) |
| 193 | return false; |
| 194 | return restriction[prefix_length - 1] == '/' || path[prefix_length] == '/'; |
[email protected] | c631b6aa | 2008-10-15 21:21:37 | [diff] [blame] | 195 | } |
| 196 | |
| 197 | // static |
[email protected] | 4b3c95dd | 2011-01-07 23:02:11 | [diff] [blame] | 198 | bool SdchManager::Dictionary::DomainMatch(const GURL& gurl, |
| 199 | const std::string& restriction) { |
| 200 | // TODO(jar): This is not precisely a domain match definition. |
| 201 | return gurl.DomainIs(restriction.data(), restriction.size()); |
[email protected] | 6a1f7dd4 | 2009-01-20 18:15:02 | [diff] [blame] | 202 | } |
[email protected] | c631b6aa | 2008-10-15 21:21:37 | [diff] [blame] | 203 | |
rdsmith | 81f60756 | 2014-11-21 18:35:16 | [diff] [blame] | 204 | bool SdchManager::Dictionary::Expired() const { |
| 205 | return clock_->Now() > expiration_; |
| 206 | } |
| 207 | |
| 208 | void SdchManager::Dictionary::SetClockForTesting( |
| 209 | scoped_ptr<base::Clock> clock) { |
| 210 | clock_ = clock.Pass(); |
| 211 | } |
| 212 | |
| 213 | SdchManager::DictionarySet::DictionarySet() {} |
| 214 | |
| 215 | SdchManager::DictionarySet::~DictionarySet() {} |
| 216 | |
| 217 | std::string SdchManager::DictionarySet::GetDictionaryClientHashList() const { |
| 218 | std::string result; |
| 219 | bool first = true; |
| 220 | for (const auto& entry: dictionaries_) { |
| 221 | if (!first) |
| 222 | result.append(","); |
| 223 | |
| 224 | result.append(entry.second->data.client_hash()); |
| 225 | first = false; |
| 226 | } |
| 227 | return result; |
| 228 | } |
| 229 | |
| 230 | const SdchManager::Dictionary* SdchManager::DictionarySet::GetDictionary( |
| 231 | const std::string& hash) const { |
| 232 | auto it = dictionaries_.find(hash); |
| 233 | if (it == dictionaries_.end()) |
| 234 | return NULL; |
| 235 | |
| 236 | return &it->second->data; |
| 237 | } |
| 238 | |
| 239 | bool SdchManager::DictionarySet::Empty() const { |
| 240 | return dictionaries_.empty(); |
| 241 | } |
| 242 | |
| 243 | void SdchManager::DictionarySet::AddDictionary( |
| 244 | const std::string& server_hash, |
| 245 | const scoped_refptr<base::RefCountedData<SdchManager::Dictionary>>& |
| 246 | dictionary) { |
| 247 | DCHECK(dictionaries_.end() == dictionaries_.find(server_hash)); |
| 248 | |
| 249 | dictionaries_[server_hash] = dictionary; |
| 250 | } |
| 251 | |
rdsmith | 47c133b | 2014-11-06 19:02:30 | [diff] [blame] | 252 | SdchManager::SdchManager() { |
| 253 | DCHECK(thread_checker_.CalledOnValidThread()); |
[email protected] | 6088942 | 2008-09-23 01:18:16 | [diff] [blame] | 254 | } |
| 255 | |
| 256 | SdchManager::~SdchManager() { |
rdsmith | 47c133b | 2014-11-06 19:02:30 | [diff] [blame] | 257 | DCHECK(thread_checker_.CalledOnValidThread()); |
[email protected] | 6088942 | 2008-09-23 01:18:16 | [diff] [blame] | 258 | while (!dictionaries_.empty()) { |
rdsmith | 81f60756 | 2014-11-21 18:35:16 | [diff] [blame] | 259 | auto it = dictionaries_.begin(); |
[email protected] | 6088942 | 2008-09-23 01:18:16 | [diff] [blame] | 260 | dictionaries_.erase(it->first); |
| 261 | } |
[email protected] | 4b3c95dd | 2011-01-07 23:02:11 | [diff] [blame] | 262 | } |
| 263 | |
[email protected] | 3de5717 | 2014-06-19 07:43:50 | [diff] [blame] | 264 | void SdchManager::ClearData() { |
| 265 | blacklisted_domains_.clear(); |
[email protected] | 3de5717 | 2014-06-19 07:43:50 | [diff] [blame] | 266 | allow_latency_experiment_.clear(); |
[email protected] | 3de5717 | 2014-06-19 07:43:50 | [diff] [blame] | 267 | dictionaries_.clear(); |
rdsmith | 47c133b | 2014-11-06 19:02:30 | [diff] [blame] | 268 | FOR_EACH_OBSERVER(SdchObserver, observers_, OnClearDictionaries(this)); |
[email protected] | 3de5717 | 2014-06-19 07:43:50 | [diff] [blame] | 269 | } |
| 270 | |
[email protected] | 4b3c95dd | 2011-01-07 23:02:11 | [diff] [blame] | 271 | // static |
baranovich | c5e3865 | 2014-11-14 03:08:15 | [diff] [blame] | 272 | void SdchManager::SdchErrorRecovery(SdchProblemCode problem) { |
| 273 | UMA_HISTOGRAM_ENUMERATION("Sdch3.ProblemCodes_5", problem, |
| 274 | SDCH_MAX_PROBLEM_CODE); |
[email protected] | 4b3c95dd | 2011-01-07 23:02:11 | [diff] [blame] | 275 | } |
| 276 | |
[email protected] | 3b543ab | 2011-09-17 21:47:00 | [diff] [blame] | 277 | // static |
| 278 | void SdchManager::EnableSdchSupport(bool enabled) { |
| 279 | g_sdch_enabled_ = enabled; |
[email protected] | 4b3c95dd | 2011-01-07 23:02:11 | [diff] [blame] | 280 | } |
| 281 | |
| 282 | // static |
[email protected] | ef99310 | 2014-01-10 19:29:37 | [diff] [blame] | 283 | void SdchManager::EnableSecureSchemeSupport(bool enabled) { |
| 284 | g_secure_scheme_supported_ = enabled; |
| 285 | } |
| 286 | |
[email protected] | 6121e11a | 2014-08-13 19:10:39 | [diff] [blame] | 287 | void SdchManager::BlacklistDomain(const GURL& url, |
baranovich | c5e3865 | 2014-11-14 03:08:15 | [diff] [blame] | 288 | SdchProblemCode blacklist_reason) { |
[email protected] | 6a58676 | 2014-06-15 16:02:22 | [diff] [blame] | 289 | SetAllowLatencyExperiment(url, false); |
[email protected] | 6a1f7dd4 | 2009-01-20 18:15:02 | [diff] [blame] | 290 | |
[email protected] | 6121e11a | 2014-08-13 19:10:39 | [diff] [blame] | 291 | BlacklistInfo* blacklist_info = |
| 292 | &blacklisted_domains_[base::StringToLowerASCII(url.host())]; |
| 293 | |
| 294 | if (blacklist_info->count > 0) |
[email protected] | 6a1f7dd4 | 2009-01-20 18:15:02 | [diff] [blame] | 295 | return; // Domain is already blacklisted. |
| 296 | |
[email protected] | 6121e11a | 2014-08-13 19:10:39 | [diff] [blame] | 297 | if (blacklist_info->exponential_count > (INT_MAX - 1) / 2) { |
| 298 | blacklist_info->exponential_count = INT_MAX; |
| 299 | } else { |
| 300 | blacklist_info->exponential_count = |
| 301 | blacklist_info->exponential_count * 2 + 1; |
| 302 | } |
[email protected] | 6a1f7dd4 | 2009-01-20 18:15:02 | [diff] [blame] | 303 | |
[email protected] | 6121e11a | 2014-08-13 19:10:39 | [diff] [blame] | 304 | blacklist_info->count = blacklist_info->exponential_count; |
| 305 | blacklist_info->reason = blacklist_reason; |
[email protected] | 6a1f7dd4 | 2009-01-20 18:15:02 | [diff] [blame] | 306 | } |
| 307 | |
[email protected] | 6121e11a | 2014-08-13 19:10:39 | [diff] [blame] | 308 | void SdchManager::BlacklistDomainForever(const GURL& url, |
baranovich | c5e3865 | 2014-11-14 03:08:15 | [diff] [blame] | 309 | SdchProblemCode blacklist_reason) { |
[email protected] | 6a58676 | 2014-06-15 16:02:22 | [diff] [blame] | 310 | SetAllowLatencyExperiment(url, false); |
[email protected] | 6a1f7dd4 | 2009-01-20 18:15:02 | [diff] [blame] | 311 | |
[email protected] | 6121e11a | 2014-08-13 19:10:39 | [diff] [blame] | 312 | BlacklistInfo* blacklist_info = |
| 313 | &blacklisted_domains_[base::StringToLowerASCII(url.host())]; |
| 314 | blacklist_info->count = INT_MAX; |
| 315 | blacklist_info->exponential_count = INT_MAX; |
| 316 | blacklist_info->reason = blacklist_reason; |
[email protected] | c631b6aa | 2008-10-15 21:21:37 | [diff] [blame] | 317 | } |
| 318 | |
[email protected] | 4b3c95dd | 2011-01-07 23:02:11 | [diff] [blame] | 319 | void SdchManager::ClearBlacklistings() { |
[email protected] | 6a58676 | 2014-06-15 16:02:22 | [diff] [blame] | 320 | blacklisted_domains_.clear(); |
[email protected] | 4b3c95dd | 2011-01-07 23:02:11 | [diff] [blame] | 321 | } |
| 322 | |
[email protected] | 4b3c95dd | 2011-01-07 23:02:11 | [diff] [blame] | 323 | void SdchManager::ClearDomainBlacklisting(const std::string& domain) { |
[email protected] | 6121e11a | 2014-08-13 19:10:39 | [diff] [blame] | 324 | BlacklistInfo* blacklist_info = &blacklisted_domains_[ |
| 325 | base::StringToLowerASCII(domain)]; |
| 326 | blacklist_info->count = 0; |
baranovich | c5e3865 | 2014-11-14 03:08:15 | [diff] [blame] | 327 | blacklist_info->reason = SDCH_OK; |
[email protected] | 4b3c95dd | 2011-01-07 23:02:11 | [diff] [blame] | 328 | } |
| 329 | |
[email protected] | 4b3c95dd | 2011-01-07 23:02:11 | [diff] [blame] | 330 | int SdchManager::BlackListDomainCount(const std::string& domain) { |
[email protected] | 6121e11a | 2014-08-13 19:10:39 | [diff] [blame] | 331 | std::string domain_lower(base::StringToLowerASCII(domain)); |
| 332 | |
| 333 | if (blacklisted_domains_.end() == blacklisted_domains_.find(domain_lower)) |
[email protected] | 4b3c95dd | 2011-01-07 23:02:11 | [diff] [blame] | 334 | return 0; |
[email protected] | 6121e11a | 2014-08-13 19:10:39 | [diff] [blame] | 335 | return blacklisted_domains_[domain_lower].count; |
[email protected] | 4b3c95dd | 2011-01-07 23:02:11 | [diff] [blame] | 336 | } |
| 337 | |
[email protected] | 4b3c95dd | 2011-01-07 23:02:11 | [diff] [blame] | 338 | int SdchManager::BlacklistDomainExponential(const std::string& domain) { |
[email protected] | 6121e11a | 2014-08-13 19:10:39 | [diff] [blame] | 339 | std::string domain_lower(base::StringToLowerASCII(domain)); |
| 340 | |
| 341 | if (blacklisted_domains_.end() == blacklisted_domains_.find(domain_lower)) |
[email protected] | 4b3c95dd | 2011-01-07 23:02:11 | [diff] [blame] | 342 | return 0; |
[email protected] | 6121e11a | 2014-08-13 19:10:39 | [diff] [blame] | 343 | return blacklisted_domains_[domain_lower].exponential_count; |
[email protected] | c631b6aa | 2008-10-15 21:21:37 | [diff] [blame] | 344 | } |
| 345 | |
baranovich | c5e3865 | 2014-11-14 03:08:15 | [diff] [blame] | 346 | SdchProblemCode SdchManager::IsInSupportedDomain(const GURL& url) { |
rdsmith | 47c133b | 2014-11-06 19:02:30 | [diff] [blame] | 347 | DCHECK(thread_checker_.CalledOnValidThread()); |
[email protected] | 3b543ab | 2011-09-17 21:47:00 | [diff] [blame] | 348 | if (!g_sdch_enabled_ ) |
baranovich | c5e3865 | 2014-11-14 03:08:15 | [diff] [blame] | 349 | return SDCH_DISABLED; |
[email protected] | c631b6aa | 2008-10-15 21:21:37 | [diff] [blame] | 350 | |
[email protected] | e1ee2748 | 2014-06-16 07:13:22 | [diff] [blame] | 351 | if (!secure_scheme_supported() && url.SchemeIsSecure()) |
baranovich | c5e3865 | 2014-11-14 03:08:15 | [diff] [blame] | 352 | return SDCH_SECURE_SCHEME_NOT_SUPPORTED; |
[email protected] | e1ee2748 | 2014-06-16 07:13:22 | [diff] [blame] | 353 | |
[email protected] | c631b6aa | 2008-10-15 21:21:37 | [diff] [blame] | 354 | if (blacklisted_domains_.empty()) |
baranovich | c5e3865 | 2014-11-14 03:08:15 | [diff] [blame] | 355 | return SDCH_OK; |
[email protected] | c631b6aa | 2008-10-15 21:21:37 | [diff] [blame] | 356 | |
[email protected] | 6121e11a | 2014-08-13 19:10:39 | [diff] [blame] | 357 | DomainBlacklistInfo::iterator it = |
| 358 | blacklisted_domains_.find(base::StringToLowerASCII(url.host())); |
| 359 | if (blacklisted_domains_.end() == it || it->second.count == 0) |
baranovich | c5e3865 | 2014-11-14 03:08:15 | [diff] [blame] | 360 | return SDCH_OK; |
[email protected] | 6a1f7dd4 | 2009-01-20 18:15:02 | [diff] [blame] | 361 | |
tzik | 2f98de30 | 2014-11-04 06:38:19 | [diff] [blame] | 362 | UMA_HISTOGRAM_ENUMERATION("Sdch3.BlacklistReason", it->second.reason, |
baranovich | c5e3865 | 2014-11-14 03:08:15 | [diff] [blame] | 363 | SDCH_MAX_PROBLEM_CODE); |
[email protected] | 6121e11a | 2014-08-13 19:10:39 | [diff] [blame] | 364 | |
| 365 | int count = it->second.count - 1; |
| 366 | if (count > 0) { |
| 367 | it->second.count = count; |
| 368 | } else { |
| 369 | it->second.count = 0; |
baranovich | c5e3865 | 2014-11-14 03:08:15 | [diff] [blame] | 370 | it->second.reason = SDCH_OK; |
[email protected] | 6121e11a | 2014-08-13 19:10:39 | [diff] [blame] | 371 | } |
| 372 | |
baranovich | c5e3865 | 2014-11-14 03:08:15 | [diff] [blame] | 373 | return SDCH_DOMAIN_BLACKLIST_INCLUDES_TARGET; |
[email protected] | 6088942 | 2008-09-23 01:18:16 | [diff] [blame] | 374 | } |
| 375 | |
baranovich | c5e3865 | 2014-11-14 03:08:15 | [diff] [blame] | 376 | SdchProblemCode SdchManager::OnGetDictionary(const GURL& request_url, |
| 377 | const GURL& dictionary_url) { |
| 378 | DCHECK(thread_checker_.CalledOnValidThread()); |
| 379 | SdchProblemCode rv = CanFetchDictionary(request_url, dictionary_url); |
| 380 | if (rv != SDCH_OK) |
| 381 | return rv; |
rdsmith | 47c133b | 2014-11-06 19:02:30 | [diff] [blame] | 382 | |
| 383 | FOR_EACH_OBSERVER(SdchObserver, |
| 384 | observers_, |
| 385 | OnGetDictionary(this, request_url, dictionary_url)); |
baranovich | c5e3865 | 2014-11-14 03:08:15 | [diff] [blame] | 386 | |
| 387 | return SDCH_OK; |
[email protected] | 4b3c95dd | 2011-01-07 23:02:11 | [diff] [blame] | 388 | } |
| 389 | |
rdsmith | a8a4e3c8 | 2015-01-08 20:18:17 | [diff] [blame] | 390 | void SdchManager::OnDictionaryUsed(const std::string& server_hash) { |
| 391 | FOR_EACH_OBSERVER(SdchObserver, observers_, |
| 392 | OnDictionaryUsed(this, server_hash)); |
| 393 | } |
| 394 | |
baranovich | c5e3865 | 2014-11-14 03:08:15 | [diff] [blame] | 395 | SdchProblemCode SdchManager::CanFetchDictionary( |
| 396 | const GURL& referring_url, |
| 397 | const GURL& dictionary_url) const { |
rdsmith | 47c133b | 2014-11-06 19:02:30 | [diff] [blame] | 398 | DCHECK(thread_checker_.CalledOnValidThread()); |
[email protected] | f0a51fb5 | 2009-03-05 12:46:38 | [diff] [blame] | 399 | /* The user agent may retrieve a dictionary from the dictionary URL if all of |
[email protected] | 6088942 | 2008-09-23 01:18:16 | [diff] [blame] | 400 | the following are true: |
[email protected] | ef99310 | 2014-01-10 19:29:37 | [diff] [blame] | 401 | 1 The dictionary URL host name matches the referrer URL host name and |
| 402 | scheme. |
[email protected] | 6088942 | 2008-09-23 01:18:16 | [diff] [blame] | 403 | 2 The dictionary URL host name domain matches the parent domain of the |
| 404 | referrer URL host name |
[email protected] | f0a51fb5 | 2009-03-05 12:46:38 | [diff] [blame] | 405 | 3 The parent domain of the referrer URL host name is not a top level |
[email protected] | 6088942 | 2008-09-23 01:18:16 | [diff] [blame] | 406 | domain |
[email protected] | 6088942 | 2008-09-23 01:18:16 | [diff] [blame] | 407 | */ |
rdsmith | 81f60756 | 2014-11-21 18:35:16 | [diff] [blame] | 408 | // Item (1) above implies item (2). Spec should be updated. |
[email protected] | 6088942 | 2008-09-23 01:18:16 | [diff] [blame] | 409 | // I take "host name match" to be "is identical to" |
[email protected] | ef99310 | 2014-01-10 19:29:37 | [diff] [blame] | 410 | if (referring_url.host() != dictionary_url.host() || |
baranovich | c5e3865 | 2014-11-14 03:08:15 | [diff] [blame] | 411 | referring_url.scheme() != dictionary_url.scheme()) |
| 412 | return SDCH_DICTIONARY_LOAD_ATTEMPT_FROM_DIFFERENT_HOST; |
| 413 | |
| 414 | if (!secure_scheme_supported() && referring_url.SchemeIsSecure()) |
| 415 | return SDCH_DICTIONARY_SELECTED_FOR_SSL; |
[email protected] | 075bc8cb | 2008-11-18 20:22:52 | [diff] [blame] | 416 | |
| 417 | // TODO(jar): Remove this failsafe conservative hack which is more restrictive |
| 418 | // than current SDCH spec when needed, and justified by security audit. |
baranovich | c5e3865 | 2014-11-14 03:08:15 | [diff] [blame] | 419 | if (!referring_url.SchemeIsHTTPOrHTTPS()) |
| 420 | return SDCH_DICTIONARY_SELECTED_FROM_NON_HTTP; |
[email protected] | 075bc8cb | 2008-11-18 20:22:52 | [diff] [blame] | 421 | |
baranovich | c5e3865 | 2014-11-14 03:08:15 | [diff] [blame] | 422 | return SDCH_OK; |
[email protected] | 075bc8cb | 2008-11-18 20:22:52 | [diff] [blame] | 423 | } |
| 424 | |
rdsmith | 81f60756 | 2014-11-21 18:35:16 | [diff] [blame] | 425 | scoped_ptr<SdchManager::DictionarySet> |
| 426 | SdchManager::GetDictionarySet(const GURL& target_url) { |
| 427 | if (IsInSupportedDomain(target_url) != SDCH_OK) |
| 428 | return NULL; |
baranovich | c5e3865 | 2014-11-14 03:08:15 | [diff] [blame] | 429 | |
[email protected] | 075bc8cb | 2008-11-18 20:22:52 | [diff] [blame] | 430 | int count = 0; |
rdsmith | 81f60756 | 2014-11-21 18:35:16 | [diff] [blame] | 431 | scoped_ptr<SdchManager::DictionarySet> result(new DictionarySet); |
| 432 | for (const auto& entry: dictionaries_) { |
| 433 | if (entry.second->data.CanUse(target_url) != SDCH_OK) |
[email protected] | 6a58676 | 2014-06-15 16:02:22 | [diff] [blame] | 434 | continue; |
rdsmith | 81f60756 | 2014-11-21 18:35:16 | [diff] [blame] | 435 | if (entry.second->data.Expired()) |
[email protected] | 6088942 | 2008-09-23 01:18:16 | [diff] [blame] | 436 | continue; |
[email protected] | 075bc8cb | 2008-11-18 20:22:52 | [diff] [blame] | 437 | ++count; |
rdsmith | 81f60756 | 2014-11-21 18:35:16 | [diff] [blame] | 438 | result->AddDictionary(entry.first, entry.second); |
[email protected] | 6088942 | 2008-09-23 01:18:16 | [diff] [blame] | 439 | } |
rdsmith | 81f60756 | 2014-11-21 18:35:16 | [diff] [blame] | 440 | |
| 441 | if (count == 0) |
| 442 | return NULL; |
| 443 | |
| 444 | UMA_HISTOGRAM_COUNTS("Sdch3.Advertisement_Count", count); |
| 445 | |
| 446 | return result.Pass(); |
| 447 | } |
| 448 | |
| 449 | scoped_ptr<SdchManager::DictionarySet> |
| 450 | SdchManager::GetDictionarySetByHash( |
| 451 | const GURL& target_url, |
| 452 | const std::string& server_hash, |
| 453 | SdchProblemCode* problem_code) { |
| 454 | scoped_ptr<SdchManager::DictionarySet> result; |
| 455 | |
| 456 | *problem_code = SDCH_DICTIONARY_HASH_NOT_FOUND; |
| 457 | const auto& it = dictionaries_.find(server_hash); |
| 458 | if (it == dictionaries_.end()) |
scottmg | d9afd548 | 2015-01-27 23:35:35 | [diff] [blame] | 459 | return result.Pass(); |
rdsmith | 81f60756 | 2014-11-21 18:35:16 | [diff] [blame] | 460 | |
| 461 | *problem_code = it->second->data.CanUse(target_url); |
| 462 | if (*problem_code != SDCH_OK) |
scottmg | d9afd548 | 2015-01-27 23:35:35 | [diff] [blame] | 463 | return result.Pass(); |
rdsmith | 81f60756 | 2014-11-21 18:35:16 | [diff] [blame] | 464 | |
| 465 | result.reset(new DictionarySet); |
| 466 | result->AddDictionary(it->first, it->second); |
scottmg | d9afd548 | 2015-01-27 23:35:35 | [diff] [blame] | 467 | return result.Pass(); |
[email protected] | 6088942 | 2008-09-23 01:18:16 | [diff] [blame] | 468 | } |
| 469 | |
[email protected] | 6088942 | 2008-09-23 01:18:16 | [diff] [blame] | 470 | // static |
| 471 | void SdchManager::GenerateHash(const std::string& dictionary_text, |
| 472 | std::string* client_hash, std::string* server_hash) { |
| 473 | char binary_hash[32]; |
[email protected] | 4b559b4d | 2011-04-14 17:37:14 | [diff] [blame] | 474 | crypto::SHA256HashString(dictionary_text, binary_hash, sizeof(binary_hash)); |
[email protected] | 6088942 | 2008-09-23 01:18:16 | [diff] [blame] | 475 | |
| 476 | std::string first_48_bits(&binary_hash[0], 6); |
| 477 | std::string second_48_bits(&binary_hash[6], 6); |
| 478 | UrlSafeBase64Encode(first_48_bits, client_hash); |
| 479 | UrlSafeBase64Encode(second_48_bits, server_hash); |
| 480 | |
[email protected] | 5b90b5d | 2009-04-30 23:06:01 | [diff] [blame] | 481 | DCHECK_EQ(server_hash->length(), 8u); |
| 482 | DCHECK_EQ(client_hash->length(), 8u); |
[email protected] | 6088942 | 2008-09-23 01:18:16 | [diff] [blame] | 483 | } |
| 484 | |
[email protected] | 5b90b5d | 2009-04-30 23:06:01 | [diff] [blame] | 485 | // Methods for supporting latency experiments. |
| 486 | |
| 487 | bool SdchManager::AllowLatencyExperiment(const GURL& url) const { |
rdsmith | 47c133b | 2014-11-06 19:02:30 | [diff] [blame] | 488 | DCHECK(thread_checker_.CalledOnValidThread()); |
[email protected] | 5b90b5d | 2009-04-30 23:06:01 | [diff] [blame] | 489 | return allow_latency_experiment_.end() != |
| 490 | allow_latency_experiment_.find(url.host()); |
| 491 | } |
| 492 | |
| 493 | void SdchManager::SetAllowLatencyExperiment(const GURL& url, bool enable) { |
rdsmith | 47c133b | 2014-11-06 19:02:30 | [diff] [blame] | 494 | DCHECK(thread_checker_.CalledOnValidThread()); |
[email protected] | 5b90b5d | 2009-04-30 23:06:01 | [diff] [blame] | 495 | if (enable) { |
| 496 | allow_latency_experiment_.insert(url.host()); |
| 497 | return; |
| 498 | } |
| 499 | ExperimentSet::iterator it = allow_latency_experiment_.find(url.host()); |
| 500 | if (allow_latency_experiment_.end() == it) |
| 501 | return; // It was already erased, or never allowed. |
baranovich | c5e3865 | 2014-11-14 03:08:15 | [diff] [blame] | 502 | SdchErrorRecovery(SDCH_LATENCY_TEST_DISALLOWED); |
[email protected] | 5b90b5d | 2009-04-30 23:06:01 | [diff] [blame] | 503 | allow_latency_experiment_.erase(it); |
| 504 | } |
[email protected] | 4b3c95dd | 2011-01-07 23:02:11 | [diff] [blame] | 505 | |
rdsmith | 47c133b | 2014-11-06 19:02:30 | [diff] [blame] | 506 | void SdchManager::AddObserver(SdchObserver* observer) { |
| 507 | observers_.AddObserver(observer); |
| 508 | } |
| 509 | |
| 510 | void SdchManager::RemoveObserver(SdchObserver* observer) { |
| 511 | observers_.RemoveObserver(observer); |
| 512 | } |
| 513 | |
baranovich | c5e3865 | 2014-11-14 03:08:15 | [diff] [blame] | 514 | SdchProblemCode SdchManager::AddSdchDictionary( |
| 515 | const std::string& dictionary_text, |
rdsmith | a8a4e3c8 | 2015-01-08 20:18:17 | [diff] [blame] | 516 | const GURL& dictionary_url, |
| 517 | std::string* server_hash_p) { |
rdsmith | 47c133b | 2014-11-06 19:02:30 | [diff] [blame] | 518 | DCHECK(thread_checker_.CalledOnValidThread()); |
rdsmith | 1df5b713 | 2014-09-09 20:36:47 | [diff] [blame] | 519 | std::string client_hash; |
| 520 | std::string server_hash; |
| 521 | GenerateHash(dictionary_text, &client_hash, &server_hash); |
baranovich | c5e3865 | 2014-11-14 03:08:15 | [diff] [blame] | 522 | if (dictionaries_.find(server_hash) != dictionaries_.end()) |
| 523 | return SDCH_DICTIONARY_ALREADY_LOADED; // Already loaded. |
rdsmith | 1df5b713 | 2014-09-09 20:36:47 | [diff] [blame] | 524 | |
| 525 | std::string domain, path; |
| 526 | std::set<int> ports; |
| 527 | base::Time expiration(base::Time::Now() + base::TimeDelta::FromDays(30)); |
| 528 | |
baranovich | c5e3865 | 2014-11-14 03:08:15 | [diff] [blame] | 529 | if (dictionary_text.empty()) |
| 530 | return SDCH_DICTIONARY_HAS_NO_TEXT; // Missing header. |
rdsmith | 1df5b713 | 2014-09-09 20:36:47 | [diff] [blame] | 531 | |
| 532 | size_t header_end = dictionary_text.find("\n\n"); |
baranovich | c5e3865 | 2014-11-14 03:08:15 | [diff] [blame] | 533 | if (std::string::npos == header_end) |
| 534 | return SDCH_DICTIONARY_HAS_NO_HEADER; // Missing header. |
| 535 | |
rdsmith | 1df5b713 | 2014-09-09 20:36:47 | [diff] [blame] | 536 | size_t line_start = 0; // Start of line being parsed. |
| 537 | while (1) { |
| 538 | size_t line_end = dictionary_text.find('\n', line_start); |
| 539 | DCHECK(std::string::npos != line_end); |
| 540 | DCHECK_LE(line_end, header_end); |
| 541 | |
| 542 | size_t colon_index = dictionary_text.find(':', line_start); |
baranovich | c5e3865 | 2014-11-14 03:08:15 | [diff] [blame] | 543 | if (std::string::npos == colon_index) |
| 544 | return SDCH_DICTIONARY_HEADER_LINE_MISSING_COLON; // Illegal line missing |
| 545 | // a colon. |
rdsmith | 1df5b713 | 2014-09-09 20:36:47 | [diff] [blame] | 546 | |
| 547 | if (colon_index > line_end) |
| 548 | break; |
| 549 | |
| 550 | size_t value_start = dictionary_text.find_first_not_of(" \t", |
| 551 | colon_index + 1); |
| 552 | if (std::string::npos != value_start) { |
| 553 | if (value_start >= line_end) |
| 554 | break; |
| 555 | std::string name(dictionary_text, line_start, colon_index - line_start); |
| 556 | std::string value(dictionary_text, value_start, line_end - value_start); |
| 557 | name = base::StringToLowerASCII(name); |
| 558 | if (name == "domain") { |
| 559 | domain = value; |
| 560 | } else if (name == "path") { |
| 561 | path = value; |
| 562 | } else if (name == "format-version") { |
| 563 | if (value != "1.0") |
baranovich | c5e3865 | 2014-11-14 03:08:15 | [diff] [blame] | 564 | return SDCH_DICTIONARY_UNSUPPORTED_VERSION; |
rdsmith | 1df5b713 | 2014-09-09 20:36:47 | [diff] [blame] | 565 | } else if (name == "max-age") { |
| 566 | int64 seconds; |
| 567 | base::StringToInt64(value, &seconds); |
| 568 | expiration = base::Time::Now() + base::TimeDelta::FromSeconds(seconds); |
| 569 | } else if (name == "port") { |
| 570 | int port; |
| 571 | base::StringToInt(value, &port); |
| 572 | if (port >= 0) |
| 573 | ports.insert(port); |
| 574 | } |
| 575 | } |
| 576 | |
| 577 | if (line_end >= header_end) |
| 578 | break; |
| 579 | line_start = line_end + 1; |
| 580 | } |
| 581 | |
rdsmith | 14fd659 | 2014-09-23 21:13:10 | [diff] [blame] | 582 | // Narrow fix for http://crbug.com/389451. |
| 583 | GURL dictionary_url_normalized(dictionary_url); |
| 584 | StripTrailingDot(&dictionary_url_normalized); |
| 585 | |
baranovich | c5e3865 | 2014-11-14 03:08:15 | [diff] [blame] | 586 | SdchProblemCode rv = IsInSupportedDomain(dictionary_url_normalized); |
| 587 | if (rv != SDCH_OK) |
| 588 | return rv; |
rdsmith | 1df5b713 | 2014-09-09 20:36:47 | [diff] [blame] | 589 | |
baranovich | c5e3865 | 2014-11-14 03:08:15 | [diff] [blame] | 590 | rv = Dictionary::CanSet(domain, path, ports, dictionary_url_normalized); |
| 591 | if (rv != SDCH_OK) |
| 592 | return rv; |
rdsmith | 1df5b713 | 2014-09-09 20:36:47 | [diff] [blame] | 593 | |
rdsmith | 1df5b713 | 2014-09-09 20:36:47 | [diff] [blame] | 594 | UMA_HISTOGRAM_COUNTS("Sdch3.Dictionary size loaded", dictionary_text.size()); |
| 595 | DVLOG(1) << "Loaded dictionary with client hash " << client_hash |
| 596 | << " and server hash " << server_hash; |
rdsmith | 81f60756 | 2014-11-21 18:35:16 | [diff] [blame] | 597 | Dictionary dictionary(dictionary_text, header_end + 2, client_hash, |
rdsmith | a8a4e3c8 | 2015-01-08 20:18:17 | [diff] [blame] | 598 | server_hash, dictionary_url_normalized, domain, path, |
| 599 | expiration, ports); |
rdsmith | 81f60756 | 2014-11-21 18:35:16 | [diff] [blame] | 600 | dictionaries_[server_hash] = |
| 601 | new base::RefCountedData<Dictionary>(dictionary); |
rdsmith | a8a4e3c8 | 2015-01-08 20:18:17 | [diff] [blame] | 602 | if (server_hash_p) |
| 603 | *server_hash_p = server_hash; |
rdsmith | 81f60756 | 2014-11-21 18:35:16 | [diff] [blame] | 604 | |
baranovich | c5e3865 | 2014-11-14 03:08:15 | [diff] [blame] | 605 | return SDCH_OK; |
rdsmith | 1df5b713 | 2014-09-09 20:36:47 | [diff] [blame] | 606 | } |
| 607 | |
rdsmith | a8a4e3c8 | 2015-01-08 20:18:17 | [diff] [blame] | 608 | SdchProblemCode SdchManager::RemoveSdchDictionary( |
| 609 | const std::string& server_hash) { |
| 610 | if (dictionaries_.find(server_hash) == dictionaries_.end()) |
| 611 | return SDCH_DICTIONARY_HASH_NOT_FOUND; |
| 612 | |
| 613 | dictionaries_.erase(server_hash); |
| 614 | return SDCH_OK; |
| 615 | } |
| 616 | |
[email protected] | 4b3c95dd | 2011-01-07 23:02:11 | [diff] [blame] | 617 | // static |
rdsmith | 81f60756 | 2014-11-21 18:35:16 | [diff] [blame] | 618 | scoped_ptr<SdchManager::DictionarySet> |
| 619 | SdchManager::CreateEmptyDictionarySetForTesting() { |
| 620 | return scoped_ptr<DictionarySet>(new DictionarySet).Pass(); |
| 621 | } |
| 622 | |
| 623 | // static |
[email protected] | 4b3c95dd | 2011-01-07 23:02:11 | [diff] [blame] | 624 | void SdchManager::UrlSafeBase64Encode(const std::string& input, |
| 625 | std::string* output) { |
| 626 | // Since this is only done during a dictionary load, and hashes are only 8 |
| 627 | // characters, we just do the simple fixup, rather than rewriting the encoder. |
| 628 | base::Base64Encode(input, output); |
[email protected] | 33acd5b | 2014-08-19 19:56:22 | [diff] [blame] | 629 | std::replace(output->begin(), output->end(), '+', '-'); |
| 630 | std::replace(output->begin(), output->end(), '/', '_'); |
[email protected] | 4b3c95dd | 2011-01-07 23:02:11 | [diff] [blame] | 631 | } |
[email protected] | ba5734e | 2011-02-01 03:42:11 | [diff] [blame] | 632 | |
baranovich | c5e3865 | 2014-11-14 03:08:15 | [diff] [blame] | 633 | base::Value* SdchManager::SdchInfoToValue() const { |
| 634 | base::DictionaryValue* value = new base::DictionaryValue(); |
| 635 | |
| 636 | value->SetBoolean("sdch_enabled", sdch_enabled()); |
| 637 | value->SetBoolean("secure_scheme_support", secure_scheme_supported()); |
| 638 | |
| 639 | base::ListValue* entry_list = new base::ListValue(); |
rdsmith | 81f60756 | 2014-11-21 18:35:16 | [diff] [blame] | 640 | for (const auto& entry: dictionaries_) { |
baranovich | c5e3865 | 2014-11-14 03:08:15 | [diff] [blame] | 641 | base::DictionaryValue* entry_dict = new base::DictionaryValue(); |
rdsmith | 81f60756 | 2014-11-21 18:35:16 | [diff] [blame] | 642 | entry_dict->SetString("url", entry.second->data.url().spec()); |
| 643 | entry_dict->SetString("client_hash", entry.second->data.client_hash()); |
| 644 | entry_dict->SetString("domain", entry.second->data.domain()); |
| 645 | entry_dict->SetString("path", entry.second->data.path()); |
baranovich | c5e3865 | 2014-11-14 03:08:15 | [diff] [blame] | 646 | base::ListValue* port_list = new base::ListValue(); |
rdsmith | 81f60756 | 2014-11-21 18:35:16 | [diff] [blame] | 647 | for (std::set<int>::const_iterator port_it = |
| 648 | entry.second->data.ports().begin(); |
| 649 | port_it != entry.second->data.ports().end(); ++port_it) { |
baranovich | c5e3865 | 2014-11-14 03:08:15 | [diff] [blame] | 650 | port_list->AppendInteger(*port_it); |
| 651 | } |
| 652 | entry_dict->Set("ports", port_list); |
rdsmith | 81f60756 | 2014-11-21 18:35:16 | [diff] [blame] | 653 | entry_dict->SetString("server_hash", entry.first); |
baranovich | c5e3865 | 2014-11-14 03:08:15 | [diff] [blame] | 654 | entry_list->Append(entry_dict); |
| 655 | } |
| 656 | value->Set("dictionaries", entry_list); |
| 657 | |
| 658 | entry_list = new base::ListValue(); |
| 659 | for (DomainBlacklistInfo::const_iterator it = blacklisted_domains_.begin(); |
| 660 | it != blacklisted_domains_.end(); ++it) { |
| 661 | if (it->second.count == 0) |
| 662 | continue; |
| 663 | base::DictionaryValue* entry_dict = new base::DictionaryValue(); |
| 664 | entry_dict->SetString("domain", it->first); |
| 665 | if (it->second.count != INT_MAX) |
| 666 | entry_dict->SetInteger("tries", it->second.count); |
| 667 | entry_dict->SetInteger("reason", it->second.reason); |
| 668 | entry_list->Append(entry_dict); |
| 669 | } |
| 670 | value->Set("blacklisted", entry_list); |
| 671 | |
| 672 | return value; |
| 673 | } |
| 674 | |
[email protected] | ba5734e | 2011-02-01 03:42:11 | [diff] [blame] | 675 | } // namespace net |