| 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" |
| (...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 71 url_(gurl), | 71 url_(gurl), |
| 72 domain_(domain), | 72 domain_(domain), |
| 73 path_(path), | 73 path_(path), |
| 74 expiration_(expiration), | 74 expiration_(expiration), |
| 75 ports_(ports) { | 75 ports_(ports) { |
| 76 } | 76 } |
| 77 | 77 |
| 78 SdchManager::Dictionary::~Dictionary() { | 78 SdchManager::Dictionary::~Dictionary() { |
| 79 } | 79 } |
| 80 | 80 |
| 81 bool SdchManager::Dictionary::CanAdvertise(const GURL& target_url) { | |
| 82 /* The specific rules of when a dictionary should be advertised in an | |
| 83 Avail-Dictionary header are modeled after the rules for cookie scoping. The | |
| 84 terms "domain-match" and "pathmatch" are defined in RFC 2965 [6]. A | |
| 85 dictionary may be advertised in the Avail-Dictionaries header exactly when | |
| 86 all of the following are true: | |
| 87 1. The server's effective host name domain-matches the Domain attribute of | |
| 88 the dictionary. | |
| 89 2. If the dictionary has a Port attribute, the request port is one of the | |
| 90 ports listed in the Port attribute. | |
| 91 3. The request URI path-matches the path header of the dictionary. | |
| 92 4. The request is not an HTTPS request. | |
| 93 We can override (ignore) item (4) only when we have explicitly enabled | |
| 94 HTTPS support AND the dictionary acquisition scheme matches the target | |
| 95 url scheme. | |
| 96 */ | |
| 97 if (!DomainMatch(target_url, domain_)) | |
| 98 return false; | |
| 99 if (!ports_.empty() && 0 == ports_.count(target_url.EffectiveIntPort())) | |
| 100 return false; | |
| 101 if (path_.size() && !PathMatch(target_url.path(), path_)) | |
| 102 return false; | |
| 103 if (!SdchManager::secure_scheme_supported() && target_url.SchemeIsSecure()) | |
| 104 return false; | |
| 105 if (target_url.SchemeIsSecure() != url_.SchemeIsSecure()) | |
| 106 return false; | |
| 107 if (base::Time::Now() > expiration_) | |
| 108 return false; | |
| 109 return true; | |
| 110 } | |
| 111 | |
| 112 //------------------------------------------------------------------------------ | 81 //------------------------------------------------------------------------------ |
| 113 // Security functions restricting loads and use of dictionaries. | 82 // Security functions restricting loads and use of dictionaries. |
| 114 | 83 |
| 115 // static | 84 // static |
| 116 bool SdchManager::Dictionary::CanSet(const std::string& domain, | 85 bool SdchManager::Dictionary::CanSet(const std::string& domain, |
| 117 const std::string& path, | 86 const std::string& path, |
| 118 const std::set<int>& ports, | 87 const std::set<int>& ports, |
| 119 const GURL& dictionary_url) { | 88 const GURL& dictionary_url) { |
| 120 /* | 89 /* |
| 121 A dictionary is invalid and must not be stored if any of the following are | 90 A dictionary is invalid and must not be stored if any of the following are |
| (...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 164 } | 133 } |
| 165 | 134 |
| 166 if (!ports.empty() | 135 if (!ports.empty() |
| 167 && 0 == ports.count(dictionary_url.EffectiveIntPort())) { | 136 && 0 == ports.count(dictionary_url.EffectiveIntPort())) { |
| 168 SdchErrorRecovery(DICTIONARY_PORT_NOT_MATCHING_SOURCE_URL); | 137 SdchErrorRecovery(DICTIONARY_PORT_NOT_MATCHING_SOURCE_URL); |
| 169 return false; | 138 return false; |
| 170 } | 139 } |
| 171 return true; | 140 return true; |
| 172 } | 141 } |
| 173 | 142 |
| 174 // static | 143 SdchManager::ProblemCodes |
| 175 bool SdchManager::Dictionary::CanUse(const GURL& referring_url) { | 144 SdchManager::Dictionary::CanUse(const GURL& target_url) { |
| 176 /* | 145 /* |
| 177 1. The request URL's host name domain-matches the Domain attribute of the | 146 1. The request URL's host name domain-matches the Domain attribute of the |
| 178 dictionary. | 147 dictionary. |
| 179 2. If the dictionary has a Port attribute, the request port is one of the | 148 2. If the dictionary has a Port attribute, the request port is one of the |
| 180 ports listed in the Port attribute. | 149 ports listed in the Port attribute. |
| 181 3. The request URL path-matches the path attribute of the dictionary. | 150 3. The request URL path-matches the path attribute of the dictionary. |
| 182 4. The request is not an HTTPS request. | 151 4. The request is not an HTTPS request. |
| 183 We can override (ignore) item (4) only when we have explicitly enabled | 152 We can override (ignore) item (4) only when we have explicitly enabled |
| 184 HTTPS support AND the dictionary acquisition scheme matches the target | 153 HTTPS support AND the dictionary acquisition scheme matches the target |
| 185 url scheme. | 154 url scheme. |
| 186 */ | 155 */ |
| 187 if (!DomainMatch(referring_url, domain_)) { | 156 if (!DomainMatch(target_url, domain_)) |
| 188 SdchErrorRecovery(DICTIONARY_FOUND_HAS_WRONG_DOMAIN); | 157 return DICTIONARY_FOUND_HAS_WRONG_DOMAIN; |
| 189 return false; | 158 if (!ports_.empty() && 0 == ports_.count(target_url.EffectiveIntPort())) |
| 190 } | 159 return DICTIONARY_FOUND_HAS_WRONG_PORT_LIST; |
| 191 if (!ports_.empty() | 160 if (path_.size() && !PathMatch(target_url.path(), path_)) |
| 192 && 0 == ports_.count(referring_url.EffectiveIntPort())) { | 161 return DICTIONARY_FOUND_HAS_WRONG_PATH; |
| 193 SdchErrorRecovery(DICTIONARY_FOUND_HAS_WRONG_PORT_LIST); | 162 if (!SdchManager::secure_scheme_supported() && target_url.SchemeIsSecure()) |
| 194 return false; | 163 return DICTIONARY_FOUND_HAS_WRONG_SCHEME; |
| 195 } | 164 if (target_url.SchemeIsSecure() != url_.SchemeIsSecure()) |
| 196 if (path_.size() && !PathMatch(referring_url.path(), path_)) { | 165 return DICTIONARY_FOUND_HAS_WRONG_SCHEME; |
| 197 SdchErrorRecovery(DICTIONARY_FOUND_HAS_WRONG_PATH); | |
| 198 return false; | |
| 199 } | |
| 200 if (!SdchManager::secure_scheme_supported() && | |
| 201 referring_url.SchemeIsSecure()) { | |
| 202 SdchErrorRecovery(DICTIONARY_FOUND_HAS_WRONG_SCHEME); | |
| 203 return false; | |
| 204 } | |
| 205 if (referring_url.SchemeIsSecure() != url_.SchemeIsSecure()) { | |
| 206 SdchErrorRecovery(DICTIONARY_FOUND_HAS_WRONG_SCHEME); | |
| 207 return false; | |
| 208 } | |
| 209 | 166 |
| 210 // TODO(jar): Remove overly restrictive failsafe test (added per security | 167 // TODO(jar): Remove overly restrictive failsafe test (added per security |
| 211 // review) when we have a need to be more general. | 168 // review) when we have a need to be more general. |
| 212 if (!referring_url.SchemeIsHTTPOrHTTPS()) { | 169 if (!target_url.SchemeIsHTTPOrHTTPS()) |
| 213 SdchErrorRecovery(ATTEMPT_TO_DECODE_NON_HTTP_DATA); | 170 return ATTEMPT_TO_DECODE_NON_HTTP_DATA; |
| 214 return false; | |
| 215 } | |
| 216 | 171 |
| 217 return true; | 172 return OK; |
| 218 } | 173 } |
| 219 | 174 |
| 220 bool SdchManager::Dictionary::PathMatch(const std::string& path, | 175 bool SdchManager::Dictionary::PathMatch(const std::string& path, |
| 221 const std::string& restriction) { | 176 const std::string& restriction) { |
| 222 /* Must be either: | 177 /* Must be either: |
| 223 1. P2 is equal to P1 | 178 1. P2 is equal to P1 |
| 224 2. P2 is a prefix of P1 and either the final character in P2 is "/" or the | 179 2. P2 is a prefix of P1 and either the final character in P2 is "/" or the |
| 225 character following P2 in P1 is "/". | 180 character following P2 in P1 is "/". |
| 226 */ | 181 */ |
| 227 if (path == restriction) | 182 if (path == restriction) |
| 228 return true; | 183 return true; |
| 229 size_t prefix_length = restriction.size(); | 184 size_t prefix_length = restriction.size(); |
| 230 if (prefix_length > path.size()) | 185 if (prefix_length > path.size()) |
| 231 return false; // Can't be a prefix. | 186 return false; // Can't be a prefix. |
| 232 if (0 != path.compare(0, prefix_length, restriction)) | 187 if (0 != path.compare(0, prefix_length, restriction)) |
| 233 return false; | 188 return false; |
| 234 return restriction[prefix_length - 1] == '/' || path[prefix_length] == '/'; | 189 return restriction[prefix_length - 1] == '/' || path[prefix_length] == '/'; |
| 235 } | 190 } |
| 236 | 191 |
| 237 // static | 192 // static |
| 238 bool SdchManager::Dictionary::DomainMatch(const GURL& gurl, | 193 bool SdchManager::Dictionary::DomainMatch(const GURL& gurl, |
| 239 const std::string& restriction) { | 194 const std::string& restriction) { |
| 240 // TODO(jar): This is not precisely a domain match definition. | 195 // TODO(jar): This is not precisely a domain match definition. |
| 241 return gurl.DomainIs(restriction.data(), restriction.size()); | 196 return gurl.DomainIs(restriction.data(), restriction.size()); |
| 242 } | 197 } |
| 243 | 198 |
| 199 bool SdchManager::Dictionary::Expired() const { |
| 200 return base::Time::Now() > expiration_; |
| 201 } |
| 202 |
| 203 //------------------------------------------------------------------------------ |
| 204 SdchManager::DictionaryWrapper::DictionaryWrapper( |
| 205 scoped_ptr<SdchManager::Dictionary> dictionary) |
| 206 : dictionary_(dictionary.Pass()) {} |
| 207 |
| 208 SdchManager::DictionaryWrapper::~DictionaryWrapper() {} |
| 209 |
| 210 //------------------------------------------------------------------------------ |
| 211 SdchManager::DictionarySet::DictionarySet() {} |
| 212 |
| 213 SdchManager::DictionarySet::~DictionarySet() {} |
| 214 |
| 215 void SdchManager::DictionarySet::GetDictionaryClientHashList( |
| 216 std::string* client_hashes) const { |
| 217 for (auto it = dictionaries_.begin(); it != dictionaries_.end(); ++it) { |
| 218 if (it != dictionaries_.begin()) |
| 219 client_hashes->append(","); |
| 220 |
| 221 client_hashes->append(it->second->dictionary()->client_hash()); |
| 222 } |
| 223 } |
| 224 |
| 225 const SdchManager::Dictionary* SdchManager::DictionarySet::Dictionary( |
| 226 const std::string& hash) const { |
| 227 auto it = dictionaries_.find(hash); |
| 228 if (it == dictionaries_.end()) |
| 229 return NULL; |
| 230 |
| 231 return it->second->dictionary(); |
| 232 } |
| 233 |
| 234 bool SdchManager::DictionarySet::Empty() const { |
| 235 return dictionaries_.empty(); |
| 236 } |
| 237 |
| 238 void SdchManager::DictionarySet::AddDictionary( |
| 239 const std::string& server_hash, |
| 240 scoped_refptr<SdchManager::DictionaryWrapper> dictionary) { |
| 241 DCHECK(dictionaries_.end() == dictionaries_.find(server_hash)); |
| 242 |
| 243 dictionaries_[server_hash] = dictionary; |
| 244 } |
| 245 |
| 244 //------------------------------------------------------------------------------ | 246 //------------------------------------------------------------------------------ |
| 245 SdchManager::SdchManager() { | 247 SdchManager::SdchManager() { |
| 246 DCHECK(thread_checker_.CalledOnValidThread()); | 248 DCHECK(thread_checker_.CalledOnValidThread()); |
| 247 } | 249 } |
| 248 | 250 |
| 249 SdchManager::~SdchManager() { | 251 SdchManager::~SdchManager() { |
| 250 DCHECK(thread_checker_.CalledOnValidThread()); | 252 DCHECK(thread_checker_.CalledOnValidThread()); |
| 251 while (!dictionaries_.empty()) { | 253 while (!dictionaries_.empty()) { |
| 252 DictionaryMap::iterator it = dictionaries_.begin(); | 254 auto it = dictionaries_.begin(); |
| 253 dictionaries_.erase(it->first); | 255 dictionaries_.erase(it->first); |
| 254 } | 256 } |
| 255 } | 257 } |
| 256 | 258 |
| 257 void SdchManager::ClearData() { | 259 void SdchManager::ClearData() { |
| 258 blacklisted_domains_.clear(); | 260 blacklisted_domains_.clear(); |
| 259 allow_latency_experiment_.clear(); | 261 allow_latency_experiment_.clear(); |
| 260 | 262 |
| 261 // Note that this may result in not having dictionaries we've advertised | 263 // Note that this may result in not having dictionaries we've advertised |
| 262 // for incoming responses. The window is relatively small (as ClearData() | 264 // for incoming responses. The window is relatively small (as ClearData() |
| (...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 315 } | 317 } |
| 316 | 318 |
| 317 void SdchManager::ClearBlacklistings() { | 319 void SdchManager::ClearBlacklistings() { |
| 318 blacklisted_domains_.clear(); | 320 blacklisted_domains_.clear(); |
| 319 } | 321 } |
| 320 | 322 |
| 321 void SdchManager::ClearDomainBlacklisting(const std::string& domain) { | 323 void SdchManager::ClearDomainBlacklisting(const std::string& domain) { |
| 322 BlacklistInfo* blacklist_info = &blacklisted_domains_[ | 324 BlacklistInfo* blacklist_info = &blacklisted_domains_[ |
| 323 base::StringToLowerASCII(domain)]; | 325 base::StringToLowerASCII(domain)]; |
| 324 blacklist_info->count = 0; | 326 blacklist_info->count = 0; |
| 325 blacklist_info->reason = MIN_PROBLEM_CODE; | 327 blacklist_info->reason = OK; |
| 326 } | 328 } |
| 327 | 329 |
| 328 int SdchManager::BlackListDomainCount(const std::string& domain) { | 330 int SdchManager::BlackListDomainCount(const std::string& domain) { |
| 329 std::string domain_lower(base::StringToLowerASCII(domain)); | 331 std::string domain_lower(base::StringToLowerASCII(domain)); |
| 330 | 332 |
| 331 if (blacklisted_domains_.end() == blacklisted_domains_.find(domain_lower)) | 333 if (blacklisted_domains_.end() == blacklisted_domains_.find(domain_lower)) |
| 332 return 0; | 334 return 0; |
| 333 return blacklisted_domains_[domain_lower].count; | 335 return blacklisted_domains_[domain_lower].count; |
| 334 } | 336 } |
| 335 | 337 |
| (...skipping 23 matching lines...) Expand all Loading... |
| 359 | 361 |
| 360 UMA_HISTOGRAM_ENUMERATION("Sdch3.BlacklistReason", it->second.reason, | 362 UMA_HISTOGRAM_ENUMERATION("Sdch3.BlacklistReason", it->second.reason, |
| 361 MAX_PROBLEM_CODE); | 363 MAX_PROBLEM_CODE); |
| 362 SdchErrorRecovery(DOMAIN_BLACKLIST_INCLUDES_TARGET); | 364 SdchErrorRecovery(DOMAIN_BLACKLIST_INCLUDES_TARGET); |
| 363 | 365 |
| 364 int count = it->second.count - 1; | 366 int count = it->second.count - 1; |
| 365 if (count > 0) { | 367 if (count > 0) { |
| 366 it->second.count = count; | 368 it->second.count = count; |
| 367 } else { | 369 } else { |
| 368 it->second.count = 0; | 370 it->second.count = 0; |
| 369 it->second.reason = MIN_PROBLEM_CODE; | 371 it->second.reason = OK; |
| 370 } | 372 } |
| 371 | 373 |
| 372 return false; | 374 return false; |
| 373 } | 375 } |
| 374 | 376 |
| 375 void SdchManager::OnGetDictionary(const GURL& request_url, | 377 void SdchManager::OnGetDictionary(const GURL& request_url, |
| 376 const GURL& dictionary_url) { | 378 const GURL& dictionary_url) { |
| 377 if (!CanFetchDictionary(request_url, dictionary_url)) | 379 if (!CanFetchDictionary(request_url, dictionary_url)) |
| 378 return; | 380 return; |
| 379 | 381 |
| (...skipping 29 matching lines...) Expand all Loading... |
| 409 // TODO(jar): Remove this failsafe conservative hack which is more restrictive | 411 // TODO(jar): Remove this failsafe conservative hack which is more restrictive |
| 410 // than current SDCH spec when needed, and justified by security audit. | 412 // than current SDCH spec when needed, and justified by security audit. |
| 411 if (!referring_url.SchemeIsHTTPOrHTTPS()) { | 413 if (!referring_url.SchemeIsHTTPOrHTTPS()) { |
| 412 SdchErrorRecovery(DICTIONARY_SELECTED_FROM_NON_HTTP); | 414 SdchErrorRecovery(DICTIONARY_SELECTED_FROM_NON_HTTP); |
| 413 return false; | 415 return false; |
| 414 } | 416 } |
| 415 | 417 |
| 416 return true; | 418 return true; |
| 417 } | 419 } |
| 418 | 420 |
| 419 void SdchManager::GetVcdiffDictionary( | 421 scoped_ptr<SdchManager::DictionarySet> |
| 420 const std::string& server_hash, | 422 SdchManager::GetDictionarySet(const GURL& target_url) { |
| 421 const GURL& referring_url, | 423 if (!IsInSupportedDomain(target_url)) |
| 422 scoped_refptr<Dictionary>* dictionary) { | 424 return NULL; |
| 423 DCHECK(thread_checker_.CalledOnValidThread()); | 425 |
| 424 *dictionary = NULL; | 426 int count = 0; |
| 425 DictionaryMap::iterator it = dictionaries_.find(server_hash); | 427 scoped_ptr<SdchManager::DictionarySet> result(new DictionarySet); |
| 426 if (it == dictionaries_.end()) { | 428 for (auto it = dictionaries_.begin(); |
| 427 return; | 429 it != dictionaries_.end(); ++it) { |
| 430 if (it->second->dictionary()->CanUse(target_url) != OK) |
| 431 continue; |
| 432 if (it->second->dictionary()->Expired()) |
| 433 continue; |
| 434 ++count; |
| 435 result->AddDictionary(it->first, it->second); |
| 428 } | 436 } |
| 429 scoped_refptr<Dictionary> matching_dictionary = it->second; | 437 |
| 430 if (!IsInSupportedDomain(referring_url)) | 438 if (count == 0) |
| 431 return; | 439 return NULL; |
| 432 if (!matching_dictionary->CanUse(referring_url)) | 440 |
| 433 return; | 441 UMA_HISTOGRAM_COUNTS("Sdch3.Advertisement_Count", count); |
| 434 *dictionary = matching_dictionary; | 442 |
| 443 return result.Pass(); |
| 435 } | 444 } |
| 436 | 445 |
| 437 // TODO(jar): If we have evictions from the dictionaries_, then we need to | 446 scoped_ptr<SdchManager::DictionarySet> |
| 438 // change this interface to return a list of reference counted Dictionary | 447 SdchManager::GetDictionarySetByHash( |
| 439 // instances that can be used if/when a server specifies one. | 448 const GURL& target_url, const std::string& server_hash) { |
| 440 void SdchManager::GetAvailDictionaryList(const GURL& target_url, | 449 scoped_ptr<SdchManager::DictionarySet> result; |
| 441 std::string* list) { | 450 |
| 442 DCHECK(thread_checker_.CalledOnValidThread()); | 451 auto it = dictionaries_.find(server_hash); |
| 443 int count = 0; | 452 if (it == dictionaries_.end()) |
| 444 for (DictionaryMap::iterator it = dictionaries_.begin(); | 453 return result; |
| 445 it != dictionaries_.end(); ++it) { | 454 |
| 446 if (!IsInSupportedDomain(target_url)) | 455 ProblemCodes ret = it->second->dictionary()->CanUse(target_url); |
| 447 continue; | 456 if (ret != OK) { |
| 448 if (!it->second->CanAdvertise(target_url)) | 457 SdchErrorRecovery(ret); |
| 449 continue; | 458 return result; |
| 450 ++count; | |
| 451 if (!list->empty()) | |
| 452 list->append(","); | |
| 453 list->append(it->second->client_hash()); | |
| 454 } | 459 } |
| 455 // Watch to see if we have corrupt or numerous dictionaries. | 460 |
| 456 if (count > 0) | 461 result.reset(new DictionarySet); |
| 457 UMA_HISTOGRAM_COUNTS("Sdch3.Advertisement_Count", count); | 462 result->AddDictionary(it->first, it->second); |
| 463 return result; |
| 458 } | 464 } |
| 459 | 465 |
| 460 // static | 466 // static |
| 461 void SdchManager::GenerateHash(const std::string& dictionary_text, | 467 void SdchManager::GenerateHash(const std::string& dictionary_text, |
| 462 std::string* client_hash, std::string* server_hash) { | 468 std::string* client_hash, std::string* server_hash) { |
| 463 char binary_hash[32]; | 469 char binary_hash[32]; |
| 464 crypto::SHA256HashString(dictionary_text, binary_hash, sizeof(binary_hash)); | 470 crypto::SHA256HashString(dictionary_text, binary_hash, sizeof(binary_hash)); |
| 465 | 471 |
| 466 std::string first_48_bits(&binary_hash[0], 6); | 472 std::string first_48_bits(&binary_hash[0], 6); |
| 467 std::string second_48_bits(&binary_hash[6], 6); | 473 std::string second_48_bits(&binary_hash[6], 6); |
| (...skipping 125 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 593 return; | 599 return; |
| 594 } | 600 } |
| 595 if (kMaxDictionaryCount <= dictionaries_.size()) { | 601 if (kMaxDictionaryCount <= dictionaries_.size()) { |
| 596 SdchErrorRecovery(DICTIONARY_COUNT_EXCEEDED); | 602 SdchErrorRecovery(DICTIONARY_COUNT_EXCEEDED); |
| 597 return; | 603 return; |
| 598 } | 604 } |
| 599 | 605 |
| 600 UMA_HISTOGRAM_COUNTS("Sdch3.Dictionary size loaded", dictionary_text.size()); | 606 UMA_HISTOGRAM_COUNTS("Sdch3.Dictionary size loaded", dictionary_text.size()); |
| 601 DVLOG(1) << "Loaded dictionary with client hash " << client_hash | 607 DVLOG(1) << "Loaded dictionary with client hash " << client_hash |
| 602 << " and server hash " << server_hash; | 608 << " and server hash " << server_hash; |
| 603 Dictionary* dictionary = | 609 scoped_ptr<Dictionary> dictionary( |
| 604 new Dictionary(dictionary_text, header_end + 2, client_hash, | 610 new Dictionary(dictionary_text, header_end + 2, client_hash, |
| 605 dictionary_url_normalized, domain, | 611 dictionary_url_normalized, domain, |
| 606 path, expiration, ports); | 612 path, expiration, ports)); |
| 607 dictionaries_[server_hash] = dictionary; | 613 dictionaries_[server_hash] = new DictionaryWrapper(dictionary.Pass()); |
| 608 return; | 614 return; |
| 609 } | 615 } |
| 610 | 616 |
| 611 // static | 617 // static |
| 618 scoped_ptr<SdchManager::DictionarySet> |
| 619 SdchManager::CreateNullDictionarySetForTesting() { |
| 620 return scoped_ptr<DictionarySet>(new DictionarySet).Pass(); |
| 621 } |
| 622 |
| 623 // static |
| 612 void SdchManager::UrlSafeBase64Encode(const std::string& input, | 624 void SdchManager::UrlSafeBase64Encode(const std::string& input, |
| 613 std::string* output) { | 625 std::string* output) { |
| 614 // Since this is only done during a dictionary load, and hashes are only 8 | 626 // Since this is only done during a dictionary load, and hashes are only 8 |
| 615 // characters, we just do the simple fixup, rather than rewriting the encoder. | 627 // characters, we just do the simple fixup, rather than rewriting the encoder. |
| 616 base::Base64Encode(input, output); | 628 base::Base64Encode(input, output); |
| 617 std::replace(output->begin(), output->end(), '+', '-'); | 629 std::replace(output->begin(), output->end(), '+', '-'); |
| 618 std::replace(output->begin(), output->end(), '/', '_'); | 630 std::replace(output->begin(), output->end(), '/', '_'); |
| 619 } | 631 } |
| 620 | 632 |
| 621 } // namespace net | 633 } // namespace net |
| OLD | NEW |