Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(808)

Side by Side Diff: net/base/sdch_manager.cc

Issue 711753003: Pin dictionaries from being deleted while request is outstanding. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Sync'd up to p303887 Created 6 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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
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
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
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
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
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
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698