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