| OLD | NEW |
| 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 257 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 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!! |
| (...skipping 128 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 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->Dictionary(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_.get()) { |
| 472 dictionary_ = unexpected_dictionary_handle_->Dictionary(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 Loading... |
| 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 |
| OLD | NEW |