OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "net/base/sdch_manager.h" | 5 #include "net/base/sdch_manager.h" |
6 | 6 |
7 #include "base/base64.h" | 7 #include "base/base64.h" |
8 #include "base/logging.h" | 8 #include "base/logging.h" |
9 #include "base/metrics/histogram.h" | 9 #include "base/metrics/histogram.h" |
10 #include "base/strings/string_number_conversions.h" | 10 #include "base/strings/string_number_conversions.h" |
11 #include "base/strings/string_util.h" | 11 #include "base/strings/string_util.h" |
12 #include "crypto/sha2.h" | 12 #include "crypto/sha2.h" |
13 #include "net/base/registry_controlled_domains/registry_controlled_domain.h" | 13 #include "net/base/registry_controlled_domains/registry_controlled_domain.h" |
| 14 #include "net/base/sdch_observer.h" |
14 #include "net/url_request/url_request_http_job.h" | 15 #include "net/url_request/url_request_http_job.h" |
15 | 16 |
16 namespace { | 17 namespace { |
17 | 18 |
18 void StripTrailingDot(GURL* gurl) { | 19 void StripTrailingDot(GURL* gurl) { |
19 std::string host(gurl->host()); | 20 std::string host(gurl->host()); |
20 | 21 |
21 if (host.empty()) | 22 if (host.empty()) |
22 return; | 23 return; |
23 | 24 |
(...skipping 20 matching lines...) Expand all Loading... |
44 // static | 45 // static |
45 const size_t SdchManager::kMaxDictionaryCount = 1; | 46 const size_t SdchManager::kMaxDictionaryCount = 1; |
46 const size_t SdchManager::kMaxDictionarySize = 500 * 1000; | 47 const size_t SdchManager::kMaxDictionarySize = 500 * 1000; |
47 #else | 48 #else |
48 // static | 49 // static |
49 const size_t SdchManager::kMaxDictionaryCount = 20; | 50 const size_t SdchManager::kMaxDictionaryCount = 20; |
50 const size_t SdchManager::kMaxDictionarySize = 1000 * 1000; | 51 const size_t SdchManager::kMaxDictionarySize = 1000 * 1000; |
51 #endif | 52 #endif |
52 | 53 |
53 // static | 54 // static |
54 #if defined(OS_IOS) | |
55 // Workaround for http://crbug.com/418975; remove when fixed. | |
56 bool SdchManager::g_sdch_enabled_ = false; | |
57 #else | |
58 bool SdchManager::g_sdch_enabled_ = true; | 55 bool SdchManager::g_sdch_enabled_ = true; |
59 #endif | |
60 | 56 |
61 // static | 57 // static |
62 bool SdchManager::g_secure_scheme_supported_ = true; | 58 bool SdchManager::g_secure_scheme_supported_ = true; |
63 | 59 |
64 //------------------------------------------------------------------------------ | 60 //------------------------------------------------------------------------------ |
65 SdchManager::Dictionary::Dictionary(const std::string& dictionary_text, | 61 SdchManager::Dictionary::Dictionary(const std::string& dictionary_text, |
66 size_t offset, | 62 size_t offset, |
67 const std::string& client_hash, | 63 const std::string& client_hash, |
68 const GURL& gurl, | 64 const GURL& gurl, |
69 const std::string& domain, | 65 const std::string& domain, |
(...skipping 169 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
239 } | 235 } |
240 | 236 |
241 // static | 237 // static |
242 bool SdchManager::Dictionary::DomainMatch(const GURL& gurl, | 238 bool SdchManager::Dictionary::DomainMatch(const GURL& gurl, |
243 const std::string& restriction) { | 239 const std::string& restriction) { |
244 // TODO(jar): This is not precisely a domain match definition. | 240 // TODO(jar): This is not precisely a domain match definition. |
245 return gurl.DomainIs(restriction.data(), restriction.size()); | 241 return gurl.DomainIs(restriction.data(), restriction.size()); |
246 } | 242 } |
247 | 243 |
248 //------------------------------------------------------------------------------ | 244 //------------------------------------------------------------------------------ |
249 SdchManager::SdchManager() | 245 SdchManager::SdchManager() { |
250 : fetches_count_for_testing_(0) { | 246 DCHECK(thread_checker_.CalledOnValidThread()); |
251 DCHECK(CalledOnValidThread()); | |
252 } | 247 } |
253 | 248 |
254 SdchManager::~SdchManager() { | 249 SdchManager::~SdchManager() { |
255 DCHECK(CalledOnValidThread()); | 250 DCHECK(thread_checker_.CalledOnValidThread()); |
256 while (!dictionaries_.empty()) { | 251 while (!dictionaries_.empty()) { |
257 DictionaryMap::iterator it = dictionaries_.begin(); | 252 DictionaryMap::iterator it = dictionaries_.begin(); |
258 dictionaries_.erase(it->first); | 253 dictionaries_.erase(it->first); |
259 } | 254 } |
260 } | 255 } |
261 | 256 |
262 void SdchManager::ClearData() { | 257 void SdchManager::ClearData() { |
263 blacklisted_domains_.clear(); | 258 blacklisted_domains_.clear(); |
264 allow_latency_experiment_.clear(); | 259 allow_latency_experiment_.clear(); |
265 if (fetcher_.get()) | |
266 fetcher_->Cancel(); | |
267 | 260 |
268 // Note that this may result in not having dictionaries we've advertised | 261 // Note that this may result in not having dictionaries we've advertised |
269 // for incoming responses. The window is relatively small (as ClearData() | 262 // for incoming responses. The window is relatively small (as ClearData() |
270 // is not expected to be called frequently), so we rely on meta-refresh | 263 // is not expected to be called frequently), so we rely on meta-refresh |
271 // to handle this case. | 264 // to handle this case. |
272 dictionaries_.clear(); | 265 dictionaries_.clear(); |
| 266 |
| 267 FOR_EACH_OBSERVER(SdchObserver, observers_, OnClearDictionaries(this)); |
273 } | 268 } |
274 | 269 |
275 // static | 270 // static |
276 void SdchManager::SdchErrorRecovery(ProblemCodes problem) { | 271 void SdchManager::SdchErrorRecovery(ProblemCodes problem) { |
277 UMA_HISTOGRAM_ENUMERATION("Sdch3.ProblemCodes_4", problem, MAX_PROBLEM_CODE); | 272 UMA_HISTOGRAM_ENUMERATION("Sdch3.ProblemCodes_4", problem, MAX_PROBLEM_CODE); |
278 } | 273 } |
279 | 274 |
280 void SdchManager::set_sdch_fetcher(scoped_ptr<SdchFetcher> fetcher) { | |
281 DCHECK(CalledOnValidThread()); | |
282 fetcher_ = fetcher.Pass(); | |
283 } | |
284 | |
285 // static | 275 // static |
286 void SdchManager::EnableSdchSupport(bool enabled) { | 276 void SdchManager::EnableSdchSupport(bool enabled) { |
287 g_sdch_enabled_ = enabled; | 277 g_sdch_enabled_ = enabled; |
288 } | 278 } |
289 | 279 |
290 // static | 280 // static |
291 void SdchManager::EnableSecureSchemeSupport(bool enabled) { | 281 void SdchManager::EnableSecureSchemeSupport(bool enabled) { |
292 g_secure_scheme_supported_ = enabled; | 282 g_secure_scheme_supported_ = enabled; |
293 } | 283 } |
294 | 284 |
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
345 | 335 |
346 int SdchManager::BlacklistDomainExponential(const std::string& domain) { | 336 int SdchManager::BlacklistDomainExponential(const std::string& domain) { |
347 std::string domain_lower(base::StringToLowerASCII(domain)); | 337 std::string domain_lower(base::StringToLowerASCII(domain)); |
348 | 338 |
349 if (blacklisted_domains_.end() == blacklisted_domains_.find(domain_lower)) | 339 if (blacklisted_domains_.end() == blacklisted_domains_.find(domain_lower)) |
350 return 0; | 340 return 0; |
351 return blacklisted_domains_[domain_lower].exponential_count; | 341 return blacklisted_domains_[domain_lower].exponential_count; |
352 } | 342 } |
353 | 343 |
354 bool SdchManager::IsInSupportedDomain(const GURL& url) { | 344 bool SdchManager::IsInSupportedDomain(const GURL& url) { |
355 DCHECK(CalledOnValidThread()); | 345 DCHECK(thread_checker_.CalledOnValidThread()); |
356 if (!g_sdch_enabled_ ) | 346 if (!g_sdch_enabled_ ) |
357 return false; | 347 return false; |
358 | 348 |
359 if (!secure_scheme_supported() && url.SchemeIsSecure()) | 349 if (!secure_scheme_supported() && url.SchemeIsSecure()) |
360 return false; | 350 return false; |
361 | 351 |
362 if (blacklisted_domains_.empty()) | 352 if (blacklisted_domains_.empty()) |
363 return true; | 353 return true; |
364 | 354 |
365 DomainBlacklistInfo::iterator it = | 355 DomainBlacklistInfo::iterator it = |
366 blacklisted_domains_.find(base::StringToLowerASCII(url.host())); | 356 blacklisted_domains_.find(base::StringToLowerASCII(url.host())); |
367 if (blacklisted_domains_.end() == it || it->second.count == 0) | 357 if (blacklisted_domains_.end() == it || it->second.count == 0) |
368 return true; | 358 return true; |
369 | 359 |
370 UMA_HISTOGRAM_ENUMERATION("Sdch3.BlacklistReason", it->second.reason, | 360 UMA_HISTOGRAM_ENUMERATION("Sdch3.BlacklistReason", it->second.reason, |
371 MAX_PROBLEM_CODE); | 361 MAX_PROBLEM_CODE); |
372 SdchErrorRecovery(DOMAIN_BLACKLIST_INCLUDES_TARGET); | 362 SdchErrorRecovery(DOMAIN_BLACKLIST_INCLUDES_TARGET); |
373 | 363 |
374 int count = it->second.count - 1; | 364 int count = it->second.count - 1; |
375 if (count > 0) { | 365 if (count > 0) { |
376 it->second.count = count; | 366 it->second.count = count; |
377 } else { | 367 } else { |
378 it->second.count = 0; | 368 it->second.count = 0; |
379 it->second.reason = MIN_PROBLEM_CODE; | 369 it->second.reason = MIN_PROBLEM_CODE; |
380 } | 370 } |
381 | 371 |
382 return false; | 372 return false; |
383 } | 373 } |
384 | 374 |
385 void SdchManager::FetchDictionary(const GURL& request_url, | 375 void SdchManager::OnGetDictionary(const GURL& request_url, |
386 const GURL& dictionary_url) { | 376 const GURL& dictionary_url) { |
387 DCHECK(CalledOnValidThread()); | 377 if (!CanFetchDictionary(request_url, dictionary_url)) |
388 if (CanFetchDictionary(request_url, dictionary_url) && fetcher_.get()) { | 378 return; |
389 ++fetches_count_for_testing_; | 379 |
390 fetcher_->Schedule(dictionary_url); | 380 FOR_EACH_OBSERVER(SdchObserver, |
391 } | 381 observers_, |
| 382 OnGetDictionary(this, request_url, dictionary_url)); |
392 } | 383 } |
393 | 384 |
394 bool SdchManager::CanFetchDictionary(const GURL& referring_url, | 385 bool SdchManager::CanFetchDictionary(const GURL& referring_url, |
395 const GURL& dictionary_url) const { | 386 const GURL& dictionary_url) const { |
396 DCHECK(CalledOnValidThread()); | 387 DCHECK(thread_checker_.CalledOnValidThread()); |
397 /* The user agent may retrieve a dictionary from the dictionary URL if all of | 388 /* The user agent may retrieve a dictionary from the dictionary URL if all of |
398 the following are true: | 389 the following are true: |
399 1 The dictionary URL host name matches the referrer URL host name and | 390 1 The dictionary URL host name matches the referrer URL host name and |
400 scheme. | 391 scheme. |
401 2 The dictionary URL host name domain matches the parent domain of the | 392 2 The dictionary URL host name domain matches the parent domain of the |
402 referrer URL host name | 393 referrer URL host name |
403 3 The parent domain of the referrer URL host name is not a top level | 394 3 The parent domain of the referrer URL host name is not a top level |
404 domain | 395 domain |
405 4 The dictionary URL is not an HTTPS URL. | |
406 */ | 396 */ |
407 // Item (1) above implies item (2). Spec should be updated. | 397 // Item (1) above implies item (2). Spec should be updated. |
408 // I take "host name match" to be "is identical to" | 398 // I take "host name match" to be "is identical to" |
409 if (referring_url.host() != dictionary_url.host() || | 399 if (referring_url.host() != dictionary_url.host() || |
410 referring_url.scheme() != dictionary_url.scheme()) { | 400 referring_url.scheme() != dictionary_url.scheme()) { |
411 SdchErrorRecovery(DICTIONARY_LOAD_ATTEMPT_FROM_DIFFERENT_HOST); | 401 SdchErrorRecovery(DICTIONARY_LOAD_ATTEMPT_FROM_DIFFERENT_HOST); |
412 return false; | 402 return false; |
413 } | 403 } |
414 if (!secure_scheme_supported() && referring_url.SchemeIsSecure()) { | 404 if (!secure_scheme_supported() && referring_url.SchemeIsSecure()) { |
415 SdchErrorRecovery(DICTIONARY_SELECTED_FOR_SSL); | 405 SdchErrorRecovery(DICTIONARY_SELECTED_FOR_SSL); |
416 return false; | 406 return false; |
417 } | 407 } |
418 | 408 |
419 // TODO(jar): Remove this failsafe conservative hack which is more restrictive | 409 // TODO(jar): Remove this failsafe conservative hack which is more restrictive |
420 // than current SDCH spec when needed, and justified by security audit. | 410 // than current SDCH spec when needed, and justified by security audit. |
421 if (!referring_url.SchemeIsHTTPOrHTTPS()) { | 411 if (!referring_url.SchemeIsHTTPOrHTTPS()) { |
422 SdchErrorRecovery(DICTIONARY_SELECTED_FROM_NON_HTTP); | 412 SdchErrorRecovery(DICTIONARY_SELECTED_FROM_NON_HTTP); |
423 return false; | 413 return false; |
424 } | 414 } |
425 | 415 |
426 return true; | 416 return true; |
427 } | 417 } |
428 | 418 |
429 void SdchManager::GetVcdiffDictionary( | 419 void SdchManager::GetVcdiffDictionary( |
430 const std::string& server_hash, | 420 const std::string& server_hash, |
431 const GURL& referring_url, | 421 const GURL& referring_url, |
432 scoped_refptr<Dictionary>* dictionary) { | 422 scoped_refptr<Dictionary>* dictionary) { |
433 DCHECK(CalledOnValidThread()); | 423 DCHECK(thread_checker_.CalledOnValidThread()); |
434 *dictionary = NULL; | 424 *dictionary = NULL; |
435 DictionaryMap::iterator it = dictionaries_.find(server_hash); | 425 DictionaryMap::iterator it = dictionaries_.find(server_hash); |
436 if (it == dictionaries_.end()) { | 426 if (it == dictionaries_.end()) { |
437 return; | 427 return; |
438 } | 428 } |
439 scoped_refptr<Dictionary> matching_dictionary = it->second; | 429 scoped_refptr<Dictionary> matching_dictionary = it->second; |
440 if (!IsInSupportedDomain(referring_url)) | 430 if (!IsInSupportedDomain(referring_url)) |
441 return; | 431 return; |
442 if (!matching_dictionary->CanUse(referring_url)) | 432 if (!matching_dictionary->CanUse(referring_url)) |
443 return; | 433 return; |
444 *dictionary = matching_dictionary; | 434 *dictionary = matching_dictionary; |
445 } | 435 } |
446 | 436 |
447 // TODO(jar): If we have evictions from the dictionaries_, then we need to | 437 // TODO(jar): If we have evictions from the dictionaries_, then we need to |
448 // change this interface to return a list of reference counted Dictionary | 438 // change this interface to return a list of reference counted Dictionary |
449 // instances that can be used if/when a server specifies one. | 439 // instances that can be used if/when a server specifies one. |
450 void SdchManager::GetAvailDictionaryList(const GURL& target_url, | 440 void SdchManager::GetAvailDictionaryList(const GURL& target_url, |
451 std::string* list) { | 441 std::string* list) { |
452 DCHECK(CalledOnValidThread()); | 442 DCHECK(thread_checker_.CalledOnValidThread()); |
453 int count = 0; | 443 int count = 0; |
454 for (DictionaryMap::iterator it = dictionaries_.begin(); | 444 for (DictionaryMap::iterator it = dictionaries_.begin(); |
455 it != dictionaries_.end(); ++it) { | 445 it != dictionaries_.end(); ++it) { |
456 if (!IsInSupportedDomain(target_url)) | 446 if (!IsInSupportedDomain(target_url)) |
457 continue; | 447 continue; |
458 if (!it->second->CanAdvertise(target_url)) | 448 if (!it->second->CanAdvertise(target_url)) |
459 continue; | 449 continue; |
460 ++count; | 450 ++count; |
461 if (!list->empty()) | 451 if (!list->empty()) |
462 list->append(","); | 452 list->append(","); |
(...skipping 16 matching lines...) Expand all Loading... |
479 UrlSafeBase64Encode(second_48_bits, server_hash); | 469 UrlSafeBase64Encode(second_48_bits, server_hash); |
480 | 470 |
481 DCHECK_EQ(server_hash->length(), 8u); | 471 DCHECK_EQ(server_hash->length(), 8u); |
482 DCHECK_EQ(client_hash->length(), 8u); | 472 DCHECK_EQ(client_hash->length(), 8u); |
483 } | 473 } |
484 | 474 |
485 //------------------------------------------------------------------------------ | 475 //------------------------------------------------------------------------------ |
486 // Methods for supporting latency experiments. | 476 // Methods for supporting latency experiments. |
487 | 477 |
488 bool SdchManager::AllowLatencyExperiment(const GURL& url) const { | 478 bool SdchManager::AllowLatencyExperiment(const GURL& url) const { |
489 DCHECK(CalledOnValidThread()); | 479 DCHECK(thread_checker_.CalledOnValidThread()); |
490 return allow_latency_experiment_.end() != | 480 return allow_latency_experiment_.end() != |
491 allow_latency_experiment_.find(url.host()); | 481 allow_latency_experiment_.find(url.host()); |
492 } | 482 } |
493 | 483 |
494 void SdchManager::SetAllowLatencyExperiment(const GURL& url, bool enable) { | 484 void SdchManager::SetAllowLatencyExperiment(const GURL& url, bool enable) { |
495 DCHECK(CalledOnValidThread()); | 485 DCHECK(thread_checker_.CalledOnValidThread()); |
496 if (enable) { | 486 if (enable) { |
497 allow_latency_experiment_.insert(url.host()); | 487 allow_latency_experiment_.insert(url.host()); |
498 return; | 488 return; |
499 } | 489 } |
500 ExperimentSet::iterator it = allow_latency_experiment_.find(url.host()); | 490 ExperimentSet::iterator it = allow_latency_experiment_.find(url.host()); |
501 if (allow_latency_experiment_.end() == it) | 491 if (allow_latency_experiment_.end() == it) |
502 return; // It was already erased, or never allowed. | 492 return; // It was already erased, or never allowed. |
503 SdchErrorRecovery(LATENCY_TEST_DISALLOWED); | 493 SdchErrorRecovery(LATENCY_TEST_DISALLOWED); |
504 allow_latency_experiment_.erase(it); | 494 allow_latency_experiment_.erase(it); |
505 } | 495 } |
506 | 496 |
| 497 void SdchManager::AddObserver(SdchObserver* observer) { |
| 498 observers_.AddObserver(observer); |
| 499 } |
| 500 |
| 501 void SdchManager::RemoveObserver(SdchObserver* observer) { |
| 502 observers_.RemoveObserver(observer); |
| 503 } |
| 504 |
507 void SdchManager::AddSdchDictionary(const std::string& dictionary_text, | 505 void SdchManager::AddSdchDictionary(const std::string& dictionary_text, |
508 const GURL& dictionary_url) { | 506 const GURL& dictionary_url) { |
509 DCHECK(CalledOnValidThread()); | 507 DCHECK(thread_checker_.CalledOnValidThread()); |
510 std::string client_hash; | 508 std::string client_hash; |
511 std::string server_hash; | 509 std::string server_hash; |
512 GenerateHash(dictionary_text, &client_hash, &server_hash); | 510 GenerateHash(dictionary_text, &client_hash, &server_hash); |
513 if (dictionaries_.find(server_hash) != dictionaries_.end()) { | 511 if (dictionaries_.find(server_hash) != dictionaries_.end()) { |
514 SdchErrorRecovery(DICTIONARY_ALREADY_LOADED); | 512 SdchErrorRecovery(DICTIONARY_ALREADY_LOADED); |
515 return; // Already loaded. | 513 return; // Already loaded. |
516 } | 514 } |
517 | 515 |
518 std::string domain, path; | 516 std::string domain, path; |
519 std::set<int> ports; | 517 std::set<int> ports; |
(...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
614 void SdchManager::UrlSafeBase64Encode(const std::string& input, | 612 void SdchManager::UrlSafeBase64Encode(const std::string& input, |
615 std::string* output) { | 613 std::string* output) { |
616 // Since this is only done during a dictionary load, and hashes are only 8 | 614 // Since this is only done during a dictionary load, and hashes are only 8 |
617 // characters, we just do the simple fixup, rather than rewriting the encoder. | 615 // characters, we just do the simple fixup, rather than rewriting the encoder. |
618 base::Base64Encode(input, output); | 616 base::Base64Encode(input, output); |
619 std::replace(output->begin(), output->end(), '+', '-'); | 617 std::replace(output->begin(), output->end(), '+', '-'); |
620 std::replace(output->begin(), output->end(), '/', '_'); | 618 std::replace(output->begin(), output->end(), '/', '_'); |
621 } | 619 } |
622 | 620 |
623 } // namespace net | 621 } // namespace net |
OLD | NEW |