OLD | NEW |
| (Empty) |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "net/base/sdch_manager.h" | |
6 | |
7 #include "base/base64.h" | |
8 #include "base/logging.h" | |
9 #include "base/metrics/histogram.h" | |
10 #include "base/strings/string_number_conversions.h" | |
11 #include "base/strings/string_util.h" | |
12 #include "base/time/default_clock.h" | |
13 #include "base/values.h" | |
14 #include "crypto/sha2.h" | |
15 #include "net/base/registry_controlled_domains/registry_controlled_domain.h" | |
16 #include "net/base/sdch_observer.h" | |
17 #include "net/url_request/url_request_http_job.h" | |
18 | |
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 | |
40 namespace net { | |
41 | |
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 | |
47 // static | |
48 bool SdchManager::g_sdch_enabled_ = true; | |
49 #endif | |
50 | |
51 // static | |
52 bool SdchManager::g_secure_scheme_supported_ = true; | |
53 | |
54 SdchManager::Dictionary::Dictionary(const std::string& dictionary_text, | |
55 size_t offset, | |
56 const std::string& client_hash, | |
57 const std::string& server_hash, | |
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), | |
65 server_hash_(server_hash), | |
66 url_(gurl), | |
67 domain_(domain), | |
68 path_(path), | |
69 expiration_(expiration), | |
70 ports_(ports), | |
71 clock_(new base::DefaultClock) { | |
72 } | |
73 | |
74 SdchManager::Dictionary::Dictionary(const SdchManager::Dictionary& rhs) | |
75 : text_(rhs.text_), | |
76 client_hash_(rhs.client_hash_), | |
77 server_hash_(rhs.server_hash_), | |
78 url_(rhs.url_), | |
79 domain_(rhs.domain_), | |
80 path_(rhs.path_), | |
81 expiration_(rhs.expiration_), | |
82 ports_(rhs.ports_), | |
83 clock_(new base::DefaultClock) { | |
84 } | |
85 | |
86 SdchManager::Dictionary::~Dictionary() {} | |
87 | |
88 // Security functions restricting loads and use of dictionaries. | |
89 | |
90 // static | |
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) { | |
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 | |
113 if (domain.empty()) | |
114 return SDCH_DICTIONARY_MISSING_DOMAIN_SPECIFIER; // Domain is required. | |
115 | |
116 if (registry_controlled_domains::GetDomainAndRegistry( | |
117 domain, registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES) | |
118 .empty()) { | |
119 return SDCH_DICTIONARY_SPECIFIES_TOP_LEVEL_DOMAIN; // domain was a TLD. | |
120 } | |
121 | |
122 if (!Dictionary::DomainMatch(dictionary_url, domain)) | |
123 return SDCH_DICTIONARY_DOMAIN_NOT_MATCHING_SOURCE_URL; | |
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('.'); | |
131 if (referrer_url_host.npos != end_of_host_index && | |
132 end_of_host_index < postfix_domain_index) { | |
133 return SDCH_DICTIONARY_REFERER_URL_HAS_DOT_IN_PREFIX; | |
134 } | |
135 } | |
136 | |
137 if (!ports.empty() && 0 == ports.count(dictionary_url.EffectiveIntPort())) | |
138 return SDCH_DICTIONARY_PORT_NOT_MATCHING_SOURCE_URL; | |
139 | |
140 return SDCH_OK; | |
141 } | |
142 | |
143 SdchProblemCode SdchManager::Dictionary::CanUse( | |
144 const GURL& target_url) const { | |
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. | |
152 We can override (ignore) item (4) only when we have explicitly enabled | |
153 HTTPS support AND the dictionary acquisition scheme matches the target | |
154 url scheme. | |
155 */ | |
156 if (!DomainMatch(target_url, domain_)) | |
157 return SDCH_DICTIONARY_FOUND_HAS_WRONG_DOMAIN; | |
158 | |
159 if (!ports_.empty() && 0 == ports_.count(target_url.EffectiveIntPort())) | |
160 return SDCH_DICTIONARY_FOUND_HAS_WRONG_PORT_LIST; | |
161 | |
162 if (path_.size() && !PathMatch(target_url.path(), path_)) | |
163 return SDCH_DICTIONARY_FOUND_HAS_WRONG_PATH; | |
164 | |
165 if (!SdchManager::secure_scheme_supported() && target_url.SchemeIsSecure()) | |
166 return SDCH_DICTIONARY_FOUND_HAS_WRONG_SCHEME; | |
167 | |
168 if (target_url.SchemeIsSecure() != url_.SchemeIsSecure()) | |
169 return SDCH_DICTIONARY_FOUND_HAS_WRONG_SCHEME; | |
170 | |
171 // TODO(jar): Remove overly restrictive failsafe test (added per security | |
172 // review) when we have a need to be more general. | |
173 if (!target_url.SchemeIsHTTPOrHTTPS()) | |
174 return SDCH_ATTEMPT_TO_DECODE_NON_HTTP_DATA; | |
175 | |
176 return SDCH_OK; | |
177 } | |
178 | |
179 // static | |
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] == '/'; | |
195 } | |
196 | |
197 // static | |
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()); | |
202 } | |
203 | |
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 | |
252 SdchManager::SdchManager() { | |
253 DCHECK(thread_checker_.CalledOnValidThread()); | |
254 } | |
255 | |
256 SdchManager::~SdchManager() { | |
257 DCHECK(thread_checker_.CalledOnValidThread()); | |
258 while (!dictionaries_.empty()) { | |
259 auto it = dictionaries_.begin(); | |
260 dictionaries_.erase(it->first); | |
261 } | |
262 } | |
263 | |
264 void SdchManager::ClearData() { | |
265 blacklisted_domains_.clear(); | |
266 allow_latency_experiment_.clear(); | |
267 dictionaries_.clear(); | |
268 FOR_EACH_OBSERVER(SdchObserver, observers_, OnClearDictionaries(this)); | |
269 } | |
270 | |
271 // static | |
272 void SdchManager::SdchErrorRecovery(SdchProblemCode problem) { | |
273 UMA_HISTOGRAM_ENUMERATION("Sdch3.ProblemCodes_5", problem, | |
274 SDCH_MAX_PROBLEM_CODE); | |
275 } | |
276 | |
277 // static | |
278 void SdchManager::EnableSdchSupport(bool enabled) { | |
279 g_sdch_enabled_ = enabled; | |
280 } | |
281 | |
282 // static | |
283 void SdchManager::EnableSecureSchemeSupport(bool enabled) { | |
284 g_secure_scheme_supported_ = enabled; | |
285 } | |
286 | |
287 void SdchManager::BlacklistDomain(const GURL& url, | |
288 SdchProblemCode blacklist_reason) { | |
289 SetAllowLatencyExperiment(url, false); | |
290 | |
291 BlacklistInfo* blacklist_info = | |
292 &blacklisted_domains_[base::StringToLowerASCII(url.host())]; | |
293 | |
294 if (blacklist_info->count > 0) | |
295 return; // Domain is already blacklisted. | |
296 | |
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 } | |
303 | |
304 blacklist_info->count = blacklist_info->exponential_count; | |
305 blacklist_info->reason = blacklist_reason; | |
306 } | |
307 | |
308 void SdchManager::BlacklistDomainForever(const GURL& url, | |
309 SdchProblemCode blacklist_reason) { | |
310 SetAllowLatencyExperiment(url, false); | |
311 | |
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; | |
317 } | |
318 | |
319 void SdchManager::ClearBlacklistings() { | |
320 blacklisted_domains_.clear(); | |
321 } | |
322 | |
323 void SdchManager::ClearDomainBlacklisting(const std::string& domain) { | |
324 BlacklistInfo* blacklist_info = &blacklisted_domains_[ | |
325 base::StringToLowerASCII(domain)]; | |
326 blacklist_info->count = 0; | |
327 blacklist_info->reason = SDCH_OK; | |
328 } | |
329 | |
330 int SdchManager::BlackListDomainCount(const std::string& domain) { | |
331 std::string domain_lower(base::StringToLowerASCII(domain)); | |
332 | |
333 if (blacklisted_domains_.end() == blacklisted_domains_.find(domain_lower)) | |
334 return 0; | |
335 return blacklisted_domains_[domain_lower].count; | |
336 } | |
337 | |
338 int SdchManager::BlacklistDomainExponential(const std::string& domain) { | |
339 std::string domain_lower(base::StringToLowerASCII(domain)); | |
340 | |
341 if (blacklisted_domains_.end() == blacklisted_domains_.find(domain_lower)) | |
342 return 0; | |
343 return blacklisted_domains_[domain_lower].exponential_count; | |
344 } | |
345 | |
346 SdchProblemCode SdchManager::IsInSupportedDomain(const GURL& url) { | |
347 DCHECK(thread_checker_.CalledOnValidThread()); | |
348 if (!g_sdch_enabled_ ) | |
349 return SDCH_DISABLED; | |
350 | |
351 if (!secure_scheme_supported() && url.SchemeIsSecure()) | |
352 return SDCH_SECURE_SCHEME_NOT_SUPPORTED; | |
353 | |
354 if (blacklisted_domains_.empty()) | |
355 return SDCH_OK; | |
356 | |
357 DomainBlacklistInfo::iterator it = | |
358 blacklisted_domains_.find(base::StringToLowerASCII(url.host())); | |
359 if (blacklisted_domains_.end() == it || it->second.count == 0) | |
360 return SDCH_OK; | |
361 | |
362 UMA_HISTOGRAM_ENUMERATION("Sdch3.BlacklistReason", it->second.reason, | |
363 SDCH_MAX_PROBLEM_CODE); | |
364 | |
365 int count = it->second.count - 1; | |
366 if (count > 0) { | |
367 it->second.count = count; | |
368 } else { | |
369 it->second.count = 0; | |
370 it->second.reason = SDCH_OK; | |
371 } | |
372 | |
373 return SDCH_DOMAIN_BLACKLIST_INCLUDES_TARGET; | |
374 } | |
375 | |
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; | |
382 | |
383 FOR_EACH_OBSERVER(SdchObserver, | |
384 observers_, | |
385 OnGetDictionary(this, request_url, dictionary_url)); | |
386 | |
387 return SDCH_OK; | |
388 } | |
389 | |
390 void SdchManager::OnDictionaryUsed(const std::string& server_hash) { | |
391 FOR_EACH_OBSERVER(SdchObserver, observers_, | |
392 OnDictionaryUsed(this, server_hash)); | |
393 } | |
394 | |
395 SdchProblemCode SdchManager::CanFetchDictionary( | |
396 const GURL& referring_url, | |
397 const GURL& dictionary_url) const { | |
398 DCHECK(thread_checker_.CalledOnValidThread()); | |
399 /* The user agent may retrieve a dictionary from the dictionary URL if all of | |
400 the following are true: | |
401 1 The dictionary URL host name matches the referrer URL host name and | |
402 scheme. | |
403 2 The dictionary URL host name domain matches the parent domain of the | |
404 referrer URL host name | |
405 3 The parent domain of the referrer URL host name is not a top level | |
406 domain | |
407 */ | |
408 // Item (1) above implies item (2). Spec should be updated. | |
409 // I take "host name match" to be "is identical to" | |
410 if (referring_url.host() != dictionary_url.host() || | |
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; | |
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. | |
419 if (!referring_url.SchemeIsHTTPOrHTTPS()) | |
420 return SDCH_DICTIONARY_SELECTED_FROM_NON_HTTP; | |
421 | |
422 return SDCH_OK; | |
423 } | |
424 | |
425 scoped_ptr<SdchManager::DictionarySet> | |
426 SdchManager::GetDictionarySet(const GURL& target_url) { | |
427 if (IsInSupportedDomain(target_url) != SDCH_OK) | |
428 return NULL; | |
429 | |
430 int count = 0; | |
431 scoped_ptr<SdchManager::DictionarySet> result(new DictionarySet); | |
432 for (const auto& entry: dictionaries_) { | |
433 if (entry.second->data.CanUse(target_url) != SDCH_OK) | |
434 continue; | |
435 if (entry.second->data.Expired()) | |
436 continue; | |
437 ++count; | |
438 result->AddDictionary(entry.first, entry.second); | |
439 } | |
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()) | |
459 return result.Pass(); | |
460 | |
461 *problem_code = it->second->data.CanUse(target_url); | |
462 if (*problem_code != SDCH_OK) | |
463 return result.Pass(); | |
464 | |
465 result.reset(new DictionarySet); | |
466 result->AddDictionary(it->first, it->second); | |
467 return result.Pass(); | |
468 } | |
469 | |
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]; | |
474 crypto::SHA256HashString(dictionary_text, binary_hash, sizeof(binary_hash)); | |
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 | |
481 DCHECK_EQ(server_hash->length(), 8u); | |
482 DCHECK_EQ(client_hash->length(), 8u); | |
483 } | |
484 | |
485 // Methods for supporting latency experiments. | |
486 | |
487 bool SdchManager::AllowLatencyExperiment(const GURL& url) const { | |
488 DCHECK(thread_checker_.CalledOnValidThread()); | |
489 return allow_latency_experiment_.end() != | |
490 allow_latency_experiment_.find(url.host()); | |
491 } | |
492 | |
493 void SdchManager::SetAllowLatencyExperiment(const GURL& url, bool enable) { | |
494 DCHECK(thread_checker_.CalledOnValidThread()); | |
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. | |
502 SdchErrorRecovery(SDCH_LATENCY_TEST_DISALLOWED); | |
503 allow_latency_experiment_.erase(it); | |
504 } | |
505 | |
506 void SdchManager::AddObserver(SdchObserver* observer) { | |
507 observers_.AddObserver(observer); | |
508 } | |
509 | |
510 void SdchManager::RemoveObserver(SdchObserver* observer) { | |
511 observers_.RemoveObserver(observer); | |
512 } | |
513 | |
514 SdchProblemCode SdchManager::AddSdchDictionary( | |
515 const std::string& dictionary_text, | |
516 const GURL& dictionary_url, | |
517 std::string* server_hash_p) { | |
518 DCHECK(thread_checker_.CalledOnValidThread()); | |
519 std::string client_hash; | |
520 std::string server_hash; | |
521 GenerateHash(dictionary_text, &client_hash, &server_hash); | |
522 if (dictionaries_.find(server_hash) != dictionaries_.end()) | |
523 return SDCH_DICTIONARY_ALREADY_LOADED; // Already loaded. | |
524 | |
525 std::string domain, path; | |
526 std::set<int> ports; | |
527 base::Time expiration(base::Time::Now() + base::TimeDelta::FromDays(30)); | |
528 | |
529 if (dictionary_text.empty()) | |
530 return SDCH_DICTIONARY_HAS_NO_TEXT; // Missing header. | |
531 | |
532 size_t header_end = dictionary_text.find("\n\n"); | |
533 if (std::string::npos == header_end) | |
534 return SDCH_DICTIONARY_HAS_NO_HEADER; // Missing header. | |
535 | |
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); | |
543 if (std::string::npos == colon_index) | |
544 return SDCH_DICTIONARY_HEADER_LINE_MISSING_COLON; // Illegal line missing | |
545 // a colon. | |
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") | |
564 return SDCH_DICTIONARY_UNSUPPORTED_VERSION; | |
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 | |
582 // Narrow fix for http://crbug.com/389451. | |
583 GURL dictionary_url_normalized(dictionary_url); | |
584 StripTrailingDot(&dictionary_url_normalized); | |
585 | |
586 SdchProblemCode rv = IsInSupportedDomain(dictionary_url_normalized); | |
587 if (rv != SDCH_OK) | |
588 return rv; | |
589 | |
590 rv = Dictionary::CanSet(domain, path, ports, dictionary_url_normalized); | |
591 if (rv != SDCH_OK) | |
592 return rv; | |
593 | |
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; | |
597 Dictionary dictionary(dictionary_text, header_end + 2, client_hash, | |
598 server_hash, dictionary_url_normalized, domain, path, | |
599 expiration, ports); | |
600 dictionaries_[server_hash] = | |
601 new base::RefCountedData<Dictionary>(dictionary); | |
602 if (server_hash_p) | |
603 *server_hash_p = server_hash; | |
604 | |
605 return SDCH_OK; | |
606 } | |
607 | |
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 | |
617 // static | |
618 scoped_ptr<SdchManager::DictionarySet> | |
619 SdchManager::CreateEmptyDictionarySetForTesting() { | |
620 return scoped_ptr<DictionarySet>(new DictionarySet).Pass(); | |
621 } | |
622 | |
623 // static | |
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); | |
629 std::replace(output->begin(), output->end(), '+', '-'); | |
630 std::replace(output->begin(), output->end(), '/', '_'); | |
631 } | |
632 | |
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(); | |
640 for (const auto& entry: dictionaries_) { | |
641 base::DictionaryValue* entry_dict = new base::DictionaryValue(); | |
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()); | |
646 base::ListValue* port_list = new base::ListValue(); | |
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) { | |
650 port_list->AppendInteger(*port_it); | |
651 } | |
652 entry_dict->Set("ports", port_list); | |
653 entry_dict->SetString("server_hash", entry.first); | |
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 | |
675 } // namespace net | |
OLD | NEW |