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

Side by Side Diff: net/filter/sdch_filter.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: Integrated Ryan's comments. 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
« no previous file with comments | « net/filter/sdch_filter.h ('k') | net/filter/sdch_filter_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2014 The Chromium Authors. All rights reserved. 1 // Copyright 2014 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/filter/sdch_filter.h" 5 #include "net/filter/sdch_filter.h"
6 6
7 #include <ctype.h> 7 #include <ctype.h>
8 #include <limits.h> 8 #include <limits.h>
9 9
10 #include <algorithm> 10 #include <algorithm>
11 11
12 #include "base/logging.h" 12 #include "base/logging.h"
13 #include "base/metrics/histogram.h" 13 #include "base/metrics/histogram.h"
14 #include "base/values.h" 14 #include "base/values.h"
15 #include "net/base/sdch_manager.h" 15 #include "net/base/sdch_manager.h"
16 #include "net/base/sdch_net_log_params.h" 16 #include "net/base/sdch_net_log_params.h"
17 #include "net/base/sdch_problem_codes.h"
17 #include "net/url_request/url_request_context.h" 18 #include "net/url_request/url_request_context.h"
18 19
19 #include "sdch/open-vcdiff/src/google/vcdecoder.h" 20 #include "sdch/open-vcdiff/src/google/vcdecoder.h"
20 21
21 namespace net { 22 namespace net {
22 23
23 namespace { 24 namespace {
24 25
25 // Disambiguate various types of responses that trigger a meta-refresh, 26 // Disambiguate various types of responses that trigger a meta-refresh,
26 // failure, or fallback to pass-through. 27 // failure, or fallback to pass-through.
(...skipping 216 matching lines...) Expand 10 before | Expand all | Expand 10 after
243 DCHECK_EQ(DECODING_ERROR, decoding_status_); 244 DCHECK_EQ(DECODING_ERROR, decoding_status_);
244 DCHECK_EQ(0u, dest_buffer_excess_index_); 245 DCHECK_EQ(0u, dest_buffer_excess_index_);
245 DCHECK(dest_buffer_excess_.empty()); 246 DCHECK(dest_buffer_excess_.empty());
246 // This is where we try very hard to do error recovery, and make this 247 // This is where we try very hard to do error recovery, and make this
247 // protocol robust in the face of proxies that do many different things. 248 // protocol robust in the face of proxies that do many different things.
248 // If we decide that things are looking very bad (too hard to recover), 249 // If we decide that things are looking very bad (too hard to recover),
249 // we may even issue a "meta-refresh" to reload the page without an SDCH 250 // we may even issue a "meta-refresh" to reload the page without an SDCH
250 // advertisement (so that we are sure we're not hurting anything). 251 // advertisement (so that we are sure we're not hurting anything).
251 // 252 //
252 // Watch out for an error page inserted by the proxy as part of a 40x 253 // Watch out for an error page inserted by the proxy as part of a 40x
253 // error response. When we see such content molestation, we certainly 254 // error response. When we see such content molestation, we certainly
254 // need to fall into the meta-refresh case. 255 // need to fall into the meta-refresh case.
255 ResponseCorruptionDetectionCause cause = RESPONSE_NONE; 256 ResponseCorruptionDetectionCause cause = RESPONSE_NONE;
256 if (filter_context_.GetResponseCode() == 404) { 257 if (filter_context_.GetResponseCode() == 404) {
257 // We could be more generous, but for now, only a "NOT FOUND" code will 258 // We could be more generous, but for now, only a "NOT FOUND" code will
258 // cause a pass through. All other bad codes will fall into a 259 // cause a pass through. All other bad codes will fall into a
259 // meta-refresh. 260 // meta-refresh.
260 LogSdchProblem(SDCH_PASS_THROUGH_404_CODE); 261 LogSdchProblem(SDCH_PASS_THROUGH_404_CODE);
261 cause = RESPONSE_404; 262 cause = RESPONSE_404;
262 decoding_status_ = PASS_THROUGH; 263 decoding_status_ = PASS_THROUGH;
263 } else if (filter_context_.GetResponseCode() != 200) { 264 } else if (filter_context_.GetResponseCode() != 200) {
264 // We need to meta-refresh, with SDCH disabled. 265 // We need to meta-refresh, with SDCH disabled.
265 cause = RESPONSE_NOT_200; 266 cause = RESPONSE_NOT_200;
266 } else if (filter_context_.IsCachedContent() 267 } else if (filter_context_.IsCachedContent()
267 && !dictionary_hash_is_plausible_) { 268 && !dictionary_hash_is_plausible_) {
268 // We must have hit the back button, and gotten content that was fetched 269 // We must have hit the back button, and gotten content that was fetched
269 // before we *really* advertised SDCH and a dictionary. 270 // before we *really* advertised SDCH and a dictionary.
270 LogSdchProblem(SDCH_PASS_THROUGH_OLD_CACHED); 271 LogSdchProblem(SDCH_PASS_THROUGH_OLD_CACHED);
271 decoding_status_ = PASS_THROUGH; 272 decoding_status_ = PASS_THROUGH;
272 cause = RESPONSE_OLD_UNENCODED; 273 cause = RESPONSE_OLD_UNENCODED;
273 } else if (possible_pass_through_) { 274 } else if (possible_pass_through_) {
274 // This is the potentially most graceful response. There really was no 275 // This is the potentially most graceful response. There really was no
275 // error. We were just overly cautious when we added a TENTATIVE_SDCH. 276 // error. We were just overly cautious when we added a TENTATIVE_SDCH.
276 // We added the sdch coding tag, and it should not have been added. 277 // We added the sdch coding tag, and it should not have been added.
277 // This can happen in server experiments, where the server decides 278 // This can happen in server experiments, where the server decides
278 // not to use sdch, even though there is a dictionary. To be 279 // not to use sdch, even though there is a dictionary. To be
279 // conservative, we locally added the tentative sdch (fearing that a 280 // conservative, we locally added the tentative sdch (fearing that a
280 // proxy stripped it!) and we must now recant (pass through). 281 // proxy stripped it!) and we must now recant (pass through).
281 // 282 //
282 // However.... just to be sure we don't get burned by proxies that 283 // However.... just to be sure we don't get burned by proxies that
283 // re-compress with gzip or other system, we can sniff to see if this 284 // re-compress with gzip or other system, we can sniff to see if this
284 // is compressed data etc. For now, we do nothing, which gets us into 285 // is compressed data etc. For now, we do nothing, which gets us into
285 // the meta-refresh result. 286 // the meta-refresh result.
286 // TODO(jar): Improve robustness by sniffing for valid text that we can 287 // TODO(jar): Improve robustness by sniffing for valid text that we can
287 // actual use re: decoding_status_ = PASS_THROUGH; 288 // actual use re: decoding_status_ = PASS_THROUGH;
288 cause = RESPONSE_TENTATIVE_SDCH; 289 cause = RESPONSE_TENTATIVE_SDCH;
289 } else if (dictionary_hash_is_plausible_) { 290 } else if (dictionary_hash_is_plausible_) {
290 // We need a meta-refresh since we don't have the dictionary. 291 // We need a meta-refresh since we don't have the dictionary.
291 // The common cause is a restart of the browser, where we try to render 292 // The common cause is a restart of the browser, where we try to render
292 // cached content that was saved when we had a dictionary. 293 // cached content that was saved when we had a dictionary.
293 cause = RESPONSE_NO_DICTIONARY; 294 cause = RESPONSE_NO_DICTIONARY;
294 } else if (filter_context_.SdchResponseExpected()) { 295 } else if (filter_context_.SdchDictionariesAdvertised()) {
295 // This is a very corrupt SDCH request response. We can't decode it. 296 // This is a very corrupt SDCH request response. We can't decode it.
296 // We'll use a meta-refresh, and get content without asking for SDCH. 297 // We'll use a meta-refresh, and get content without asking for SDCH.
297 // This will also progressively disable SDCH for this domain. 298 // This will also progressively disable SDCH for this domain.
298 cause = RESPONSE_CORRUPT_SDCH; 299 cause = RESPONSE_CORRUPT_SDCH;
299 } else { 300 } else {
300 // One of the first 9 bytes precluded consideration as a hash. 301 // One of the first 9 bytes precluded consideration as a hash.
301 // This can't be an SDCH payload, even though the server said it was. 302 // This can't be an SDCH payload, even though the server said it was.
302 // This is a major error, as the server or proxy tagged this SDCH even 303 // This is a major error, as the server or proxy tagged this SDCH even
303 // though it is not! 304 // though it is not!
304 // Meta-refresh won't help, as we didn't advertise an SDCH dictionary!! 305 // Meta-refresh won't help, as we didn't advertise an SDCH dictionary!!
305 // Worse yet, meta-refresh could lead to an infinite refresh loop. 306 // Worse yet, meta-refresh could lead to an infinite refresh loop.
(...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after
369 available_space -= amount; 370 available_space -= amount;
370 DCHECK_GE(available_space, 0); 371 DCHECK_GE(available_space, 0);
371 372
372 if (available_space <= 0) 373 if (available_space <= 0)
373 return FILTER_OK; 374 return FILTER_OK;
374 DCHECK(dest_buffer_excess_.empty()); 375 DCHECK(dest_buffer_excess_.empty());
375 DCHECK_EQ(0u, dest_buffer_excess_index_); 376 DCHECK_EQ(0u, dest_buffer_excess_index_);
376 377
377 if (decoding_status_ != DECODING_IN_PROGRESS) { 378 if (decoding_status_ != DECODING_IN_PROGRESS) {
378 if (META_REFRESH_RECOVERY == decoding_status_) { 379 if (META_REFRESH_RECOVERY == decoding_status_) {
379 // Absorb all input data. We've already output page reload HTML. 380 // Absorb all input data. We've already output page reload HTML.
380 next_stream_data_ = NULL; 381 next_stream_data_ = NULL;
381 stream_data_len_ = 0; 382 stream_data_len_ = 0;
382 return FILTER_NEED_MORE_DATA; 383 return FILTER_NEED_MORE_DATA;
383 } 384 }
384 if (PASS_THROUGH == decoding_status_) { 385 if (PASS_THROUGH == decoding_status_) {
385 // We must pass in available_space, but it will be changed to bytes_used. 386 // We must pass in available_space, but it will be changed to bytes_used.
386 FilterStatus result = CopyOut(dest_buffer, &available_space); 387 FilterStatus result = CopyOut(dest_buffer, &available_space);
387 // Accumulate the returned count of bytes_used (a.k.a., available_space). 388 // Accumulate the returned count of bytes_used (a.k.a., available_space).
388 *dest_len += available_space; 389 *dest_len += available_space;
389 return result; 390 return result;
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after
433 } 434 }
434 dictionary_hash_.append(next_stream_data_, bytes_needed); 435 dictionary_hash_.append(next_stream_data_, bytes_needed);
435 DCHECK(kServerIdLength == dictionary_hash_.size()); 436 DCHECK(kServerIdLength == dictionary_hash_.size());
436 stream_data_len_ -= bytes_needed; 437 stream_data_len_ -= bytes_needed;
437 DCHECK_LE(0, stream_data_len_); 438 DCHECK_LE(0, stream_data_len_);
438 if (stream_data_len_ > 0) 439 if (stream_data_len_ > 0)
439 next_stream_data_ += bytes_needed; 440 next_stream_data_ += bytes_needed;
440 else 441 else
441 next_stream_data_ = NULL; 442 next_stream_data_ = NULL;
442 443
443 DCHECK(!dictionary_.get()); 444 DCHECK(!dictionary_);
444 dictionary_hash_is_plausible_ = true; // Assume plausible, but check. 445 dictionary_hash_is_plausible_ = true; // Assume plausible, but check.
445 446
446 SdchProblemCode rv = SDCH_OK; 447 SdchProblemCode rv = SDCH_OK;
447 if ('\0' == dictionary_hash_[kServerIdLength - 1]) { 448 if ('\0' == dictionary_hash_[kServerIdLength - 1]) {
448 SdchManager* manager(url_request_context_->sdch_manager()); 449 std::string server_hash(dictionary_hash_, 0, kServerIdLength - 1);
449 rv = manager->GetVcdiffDictionary( 450 SdchManager::DictionarySet* handle =
450 std::string(dictionary_hash_, 0, kServerIdLength - 1), url_, 451 filter_context_.SdchDictionariesAdvertised();
451 &dictionary_); 452 if (handle)
452 if (rv == SDCH_DICTIONARY_HASH_NOT_FOUND) { 453 dictionary_ = handle->GetDictionary(server_hash);
453 DCHECK(dictionary_hash_.size() == kServerIdLength); 454 if (!dictionary_) {
454 // Since dictionary was not found, check to see if hash was even 455 // This is a hack. Naively, the dictionaries available for
455 // plausible. 456 // decoding should be only the ones advertised. However, there are
456 for (size_t i = 0; i < kServerIdLength - 1; ++i) { 457 // cases, specifically resources encoded with old dictionaries living
457 char base64_char = dictionary_hash_[i]; 458 // in the cache, that mean the full set of dictionaries should be made
458 if (!isalnum(base64_char) && '-' != base64_char && '_' != base64_char) { 459 // available for decoding. It's not known how often this happens;
459 rv = SDCH_DICTIONARY_HASH_MALFORMED; 460 // if it happens rarely enough, this code can be removed.
460 dictionary_hash_is_plausible_ = false; 461 //
461 break; 462 // TODO(rdsmith): Long-term, a better solution is necessary, since
463 // an entry in the cache being encoded with the dictionary doesn't
464 // guarantee that the dictionary is present. That solution probably
465 // involves storing unencoded resources in the cache, but might
466 // involve evicting encoded resources on dictionary removal.
467 // See http://crbug.com/383405.
468 unexpected_dictionary_handle_ =
469 url_request_context_->sdch_manager()->GetDictionarySetByHash(
470 url_, server_hash, &rv);
471 if (unexpected_dictionary_handle_) {
472 dictionary_ = unexpected_dictionary_handle_->GetDictionary(server_hash);
473 // Override SDCH_OK rv; this is still worth logging.
474 rv = (filter_context_.IsCachedContent() ?
475 SDCH_UNADVERTISED_DICTIONARY_USED_CACHED :
476 SDCH_UNADVERTISED_DICTIONARY_USED);
477 } else {
478 // Since dictionary was not found, check to see if hash was
479 // even plausible.
480 DCHECK(dictionary_hash_.size() == kServerIdLength);
481 rv = SDCH_DICTIONARY_HASH_NOT_FOUND;
482 for (size_t i = 0; i < kServerIdLength - 1; ++i) {
483 char base64_char = dictionary_hash_[i];
484 if (!isalnum(base64_char) &&
485 '-' != base64_char && '_' != base64_char) {
486 dictionary_hash_is_plausible_ = false;
487 rv = SDCH_DICTIONARY_HASH_MALFORMED;
488 break;
489 }
462 } 490 }
463 } 491 }
464 } 492 }
465 } else { 493 } else {
466 dictionary_hash_is_plausible_ = false; 494 dictionary_hash_is_plausible_ = false;
467 rv = SDCH_DICTIONARY_HASH_MALFORMED; 495 rv = SDCH_DICTIONARY_HASH_MALFORMED;
468 } 496 }
469 if (rv != SDCH_OK) { 497
498 if (rv != SDCH_OK)
470 LogSdchProblem(rv); 499 LogSdchProblem(rv);
500
501 if (!dictionary_) {
471 decoding_status_ = DECODING_ERROR; 502 decoding_status_ = DECODING_ERROR;
472 return FILTER_ERROR; 503 return FILTER_ERROR;
473 } 504 }
474 DCHECK(dictionary_.get()); 505
475 vcdiff_streaming_decoder_.reset(new open_vcdiff::VCDiffStreamingDecoder); 506 vcdiff_streaming_decoder_.reset(new open_vcdiff::VCDiffStreamingDecoder);
476 vcdiff_streaming_decoder_->SetAllowVcdTarget(false); 507 vcdiff_streaming_decoder_->SetAllowVcdTarget(false);
477 vcdiff_streaming_decoder_->StartDecoding(dictionary_->text().data(), 508 vcdiff_streaming_decoder_->StartDecoding(dictionary_->text().data(),
478 dictionary_->text().size()); 509 dictionary_->text().size());
479 decoding_status_ = DECODING_IN_PROGRESS; 510 decoding_status_ = DECODING_IN_PROGRESS;
480 return FILTER_OK; 511 return FILTER_OK;
481 } 512 }
482 513
483 int SdchFilter::OutputBufferExcess(char* const dest_buffer, 514 int SdchFilter::OutputBufferExcess(char* const dest_buffer,
484 size_t available_space) { 515 size_t available_space) {
(...skipping 14 matching lines...) Expand all
499 } 530 }
500 531
501 void SdchFilter::LogSdchProblem(SdchProblemCode problem) { 532 void SdchFilter::LogSdchProblem(SdchProblemCode problem) {
502 SdchManager::SdchErrorRecovery(problem); 533 SdchManager::SdchErrorRecovery(problem);
503 filter_context_.GetNetLog().AddEvent( 534 filter_context_.GetNetLog().AddEvent(
504 NetLog::TYPE_SDCH_DECODING_ERROR, 535 NetLog::TYPE_SDCH_DECODING_ERROR,
505 base::Bind(&NetLogSdchResourceProblemCallback, problem)); 536 base::Bind(&NetLogSdchResourceProblemCallback, problem));
506 } 537 }
507 538
508 } // namespace net 539 } // namespace net
OLDNEW
« no previous file with comments | « net/filter/sdch_filter.h ('k') | net/filter/sdch_filter_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698