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