172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen// Copyright (c) 2011 The Chromium Authors. All rights reserved. 2c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// Use of this source code is governed by a BSD-style license that can be 3c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// found in the LICENSE file. 4c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "net/base/sdch_filter.h" 672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 7c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#include <limits.h> 8c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#include <ctype.h> 9c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#include <algorithm> 10c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 11c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#include "base/file_util.h" 12c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#include "base/logging.h" 13731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick#include "base/metrics/histogram.h" 14c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#include "net/base/sdch_manager.h" 15c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 16c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#include "sdch/open-vcdiff/src/google/vcdecoder.h" 17c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 1872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsennamespace net { 1972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 20c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick ScottSdchFilter::SdchFilter(const FilterContext& filter_context) 21ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen : filter_context_(filter_context), 22c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott decoding_status_(DECODING_UNINITIALIZED), 23c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott vcdiff_streaming_decoder_(NULL), 24c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott dictionary_hash_(), 25c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott dictionary_hash_is_plausible_(false), 26c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott dictionary_(NULL), 27c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott dest_buffer_excess_(), 28c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott dest_buffer_excess_index_(0), 29c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott source_bytes_(0), 30c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott output_bytes_(0), 31c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott possible_pass_through_(false) { 32c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott bool success = filter_context.GetMimeType(&mime_type_); 33c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott DCHECK(success); 34c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott success = filter_context.GetURL(&url_); 35c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott DCHECK(success); 36c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott} 37c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 38c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick ScottSdchFilter::~SdchFilter() { 39c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott // All code here is for gathering stats, and can be removed when SDCH is 40c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott // considered stable. 41c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 42c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott static int filter_use_count = 0; 43c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott ++filter_use_count; 44c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott if (META_REFRESH_RECOVERY == decoding_status_) { 45c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott UMA_HISTOGRAM_COUNTS("Sdch3.FilterUseBeforeDisabling", filter_use_count); 46c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott } 47c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 48c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott if (vcdiff_streaming_decoder_.get()) { 49c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott if (!vcdiff_streaming_decoder_->FinishDecoding()) { 50c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott decoding_status_ = DECODING_ERROR; 51c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott SdchManager::SdchErrorRecovery(SdchManager::INCOMPLETE_SDCH_CONTENT); 52c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott // Make it possible for the user to hit reload, and get non-sdch content. 53c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott // Note this will "wear off" quickly enough, and is just meant to assure 54c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott // in some rare case that the user is not stuck. 55c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott SdchManager::BlacklistDomain(url_); 56c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott UMA_HISTOGRAM_COUNTS("Sdch3.PartialBytesIn", 57ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen static_cast<int>(filter_context_.GetByteReadCount())); 58c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott UMA_HISTOGRAM_COUNTS("Sdch3.PartialVcdiffIn", source_bytes_); 59c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott UMA_HISTOGRAM_COUNTS("Sdch3.PartialVcdiffOut", output_bytes_); 60c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott } 61c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott } 62c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 63c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott if (!dest_buffer_excess_.empty()) { 64c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott // Filter chaining error, or premature teardown. 65c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott SdchManager::SdchErrorRecovery(SdchManager::UNFLUSHED_CONTENT); 66c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott UMA_HISTOGRAM_COUNTS("Sdch3.UnflushedBytesIn", 67ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen static_cast<int>(filter_context_.GetByteReadCount())); 68c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott UMA_HISTOGRAM_COUNTS("Sdch3.UnflushedBufferSize", 69c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott dest_buffer_excess_.size()); 70c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott UMA_HISTOGRAM_COUNTS("Sdch3.UnflushedVcdiffIn", source_bytes_); 71c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott UMA_HISTOGRAM_COUNTS("Sdch3.UnflushedVcdiffOut", output_bytes_); 72c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott } 73c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 74ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen if (filter_context_.IsCachedContent()) { 75c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott // Not a real error, but it is useful to have this tally. 76c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott // TODO(jar): Remove this stat after SDCH stability is validated. 77c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott SdchManager::SdchErrorRecovery(SdchManager::CACHE_DECODED); 78c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott return; // We don't need timing stats, and we aready got ratios. 79c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott } 80c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 81c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott switch (decoding_status_) { 82c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott case DECODING_IN_PROGRESS: { 83c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott if (output_bytes_) 84c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott UMA_HISTOGRAM_PERCENTAGE("Sdch3.Network_Decode_Ratio_a", 85c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott static_cast<int>( 86ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen (filter_context_.GetByteReadCount() * 100) / output_bytes_)); 87c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott UMA_HISTOGRAM_COUNTS("Sdch3.Network_Decode_Bytes_VcdiffOut_a", 88c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott output_bytes_); 89ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen filter_context_.RecordPacketStats(FilterContext::SDCH_DECODE); 90c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 91c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott // Allow latency experiments to proceed. 92c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott SdchManager::Global()->SetAllowLatencyExperiment(url_, true); 93c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott return; 94c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott } 95c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott case PASS_THROUGH: { 96ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen filter_context_.RecordPacketStats(FilterContext::SDCH_PASSTHROUGH); 97c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott return; 98c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott } 99c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott case DECODING_UNINITIALIZED: { 100c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott SdchManager::SdchErrorRecovery(SdchManager::UNINITIALIZED); 101c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott return; 102c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott } 103c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott case WAITING_FOR_DICTIONARY_SELECTION: { 104c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott SdchManager::SdchErrorRecovery(SdchManager::PRIOR_TO_DICTIONARY); 105c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott return; 106c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott } 107c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott case DECODING_ERROR: { 108c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott SdchManager::SdchErrorRecovery(SdchManager::DECODE_ERROR); 109c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott return; 110c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott } 111c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott case META_REFRESH_RECOVERY: { 112c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott // Already accounted for when set. 113c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott return; 114c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott } 115c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott } // end of switch. 116c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott} 117c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 118c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottbool SdchFilter::InitDecoding(Filter::FilterType filter_type) { 119c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott if (decoding_status_ != DECODING_UNINITIALIZED) 120c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott return false; 121c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 122c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott // Handle case where sdch filter is guessed, but not required. 123c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott if (FILTER_TYPE_SDCH_POSSIBLE == filter_type) 124c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott possible_pass_through_ = true; 125c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 126c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott // Initialize decoder only after we have a dictionary in hand. 127c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott decoding_status_ = WAITING_FOR_DICTIONARY_SELECTION; 128c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott return true; 129c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott} 130c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 131731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick#ifndef NDEBUG 132c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottstatic const char* kDecompressionErrorHtml = 133c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott "<head><META HTTP-EQUIV=\"Refresh\" CONTENT=\"0\"></head>" 134c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott "<div style=\"position:fixed;top:0;left:0;width:100%;border-width:thin;" 135c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott "border-color:black;border-style:solid;text-align:left;font-family:arial;" 136c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott "font-size:10pt;foreground-color:black;background-color:white\">" 137c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott "An error occurred. This page will be reloaded shortly. " 138c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott "Or press the \"reload\" button now to reload it immediately." 139c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott "</div>"; 140731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick#else 141731df977c0511bca2206b5f333555b1205ff1f43Iain Merrickstatic const char* kDecompressionErrorHtml = 142731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick "<head><META HTTP-EQUIV=\"Refresh\" CONTENT=\"0\"></head>"; 143731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick#endif 144c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 145c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick ScottFilter::FilterStatus SdchFilter::ReadFilteredData(char* dest_buffer, 146c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott int* dest_len) { 147c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott int available_space = *dest_len; 148c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott *dest_len = 0; // Nothing output yet. 149c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 150c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott if (!dest_buffer || available_space <= 0) 151c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott return FILTER_ERROR; 152c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 153c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott if (WAITING_FOR_DICTIONARY_SELECTION == decoding_status_) { 154c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott FilterStatus status = InitializeDictionary(); 155c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott if (FILTER_NEED_MORE_DATA == status) 156c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott return FILTER_NEED_MORE_DATA; 157c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott if (FILTER_ERROR == status) { 158c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott DCHECK(DECODING_ERROR == decoding_status_); 159c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott DCHECK_EQ(0u, dest_buffer_excess_index_); 160c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott DCHECK(dest_buffer_excess_.empty()); 161c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott // This is where we try very hard to do error recovery, and make this 162c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott // protocol robust in the face of proxies that do many different things. 163c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott // If we decide that things are looking very bad (too hard to recover), 164c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott // we may even issue a "meta-refresh" to reload the page without an SDCH 165c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott // advertisement (so that we are sure we're not hurting anything). 166c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott // 167c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott // Watch out for an error page inserted by the proxy as part of a 40x 168c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott // error response. When we see such content molestation, we certainly 169c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott // need to fall into the meta-refresh case. 170ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen if (filter_context_.GetResponseCode() == 404) { 171c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott // We could be more generous, but for now, only a "NOT FOUND" code will 172c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott // cause a pass through. All other bad codes will fall into a 173c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott // meta-refresh. 174c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott SdchManager::SdchErrorRecovery(SdchManager::PASS_THROUGH_404_CODE); 175c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott decoding_status_ = PASS_THROUGH; 176ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen } else if (filter_context_.GetResponseCode() != 200) { 177c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott // We need to meta-refresh, with SDCH disabled. 178ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen } else if (filter_context_.IsCachedContent() 179c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott && !dictionary_hash_is_plausible_) { 180c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott // We must have hit the back button, and gotten content that was fetched 181c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott // before we *really* advertised SDCH and a dictionary. 182c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott SdchManager::SdchErrorRecovery(SdchManager::PASS_THROUGH_OLD_CACHED); 183c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott decoding_status_ = PASS_THROUGH; 184c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott } else if (possible_pass_through_) { 185c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott // This is the potentially most graceful response. There really was no 186c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott // error. We were just overly cautious when we added a TENTATIVE_SDCH. 187c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott // We added the sdch coding tag, and it should not have been added. 188c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott // This can happen in server experiments, where the server decides 189c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott // not to use sdch, even though there is a dictionary. To be 190c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott // conservative, we locally added the tentative sdch (fearing that a 191c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott // proxy stripped it!) and we must now recant (pass through). 192c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott SdchManager::SdchErrorRecovery(SdchManager::DISCARD_TENTATIVE_SDCH); 193c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott // However.... just to be sure we don't get burned by proxies that 194c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott // re-compress with gzip or other system, we can sniff to see if this 195c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott // is compressed data etc. For now, we do nothing, which gets us into 196c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott // the meta-refresh result. 197c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott // TODO(jar): Improve robustness by sniffing for valid text that we can 198c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott // actual use re: decoding_status_ = PASS_THROUGH; 199c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott } else if (dictionary_hash_is_plausible_) { 200c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott // We need a meta-refresh since we don't have the dictionary. 201c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott // The common cause is a restart of the browser, where we try to render 202c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott // cached content that was saved when we had a dictionary. 203ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen } else if (filter_context_.IsSdchResponse()) { 204c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott // This is a very corrupt SDCH request response. We can't decode it. 205c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott // We'll use a meta-refresh, and get content without asking for SDCH. 206c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott // This will also progressively disable SDCH for this domain. 207c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott } else { 208c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott // One of the first 9 bytes precluded consideration as a hash. 209c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott // This can't be an SDCH payload, even though the server said it was. 210c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott // This is a major error, as the server or proxy tagged this SDCH even 211c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott // though it is not! 212c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott // Meta-refresh won't help, as we didn't advertise an SDCH dictionary!! 213c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott // Worse yet, meta-refresh could lead to an infinite refresh loop. 214c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott SdchManager::SdchErrorRecovery(SdchManager::PASSING_THROUGH_NON_SDCH); 215c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott decoding_status_ = PASS_THROUGH; 216c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott // ... but further back-off on advertising SDCH support. 217c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott SdchManager::BlacklistDomain(url_); 218c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott } 219c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 220c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott if (decoding_status_ == PASS_THROUGH) { 221c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott dest_buffer_excess_ = dictionary_hash_; // Send what we scanned. 222c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott } else { 223c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott // This is where we try to do the expensive meta-refresh. 224c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott if (std::string::npos == mime_type_.find("text/html")) { 225c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott // Since we can't do a meta-refresh (along with an exponential 226c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott // backoff), we'll just make sure this NEVER happens again. 227c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott SdchManager::BlacklistDomainForever(url_); 228ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen if (filter_context_.IsCachedContent()) 229c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott SdchManager::SdchErrorRecovery( 230c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott SdchManager::CACHED_META_REFRESH_UNSUPPORTED); 231c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott else 232c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott SdchManager::SdchErrorRecovery( 233c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott SdchManager::META_REFRESH_UNSUPPORTED); 234c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott return FILTER_ERROR; 235c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott } 236c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott // HTML content means we can issue a meta-refresh, and get the content 237c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott // again, perhaps without SDCH (to be safe). 238ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen if (filter_context_.IsCachedContent()) { 239c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott // Cached content is probably a startup tab, so we'll just get fresh 240c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott // content and try again, without disabling sdch. 241c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott SdchManager::SdchErrorRecovery( 242c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott SdchManager::META_REFRESH_CACHED_RECOVERY); 243c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott } else { 244c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott // Since it wasn't in the cache, we definately need at least some 245c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott // period of blacklisting to get the correct content. 246c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott SdchManager::BlacklistDomain(url_); 247c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott SdchManager::SdchErrorRecovery(SdchManager::META_REFRESH_RECOVERY); 248c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott } 249c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott decoding_status_ = META_REFRESH_RECOVERY; 250c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott // Issue a meta redirect with SDCH disabled. 251c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott dest_buffer_excess_ = kDecompressionErrorHtml; 252c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott } 253c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott } else { 254c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott DCHECK(DECODING_IN_PROGRESS == decoding_status_); 255c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott } 256c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott } 257c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 258c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott int amount = OutputBufferExcess(dest_buffer, available_space); 259c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott *dest_len += amount; 260c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott dest_buffer += amount; 261c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott available_space -= amount; 262c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott DCHECK_GE(available_space, 0); 263c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 264c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott if (available_space <= 0) 265c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott return FILTER_OK; 266c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott DCHECK(dest_buffer_excess_.empty()); 267c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott DCHECK_EQ(0u, dest_buffer_excess_index_); 268c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 269c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott if (decoding_status_ != DECODING_IN_PROGRESS) { 270c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott if (META_REFRESH_RECOVERY == decoding_status_) { 271c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott // Absorb all input data. We've already output page reload HTML. 272c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott next_stream_data_ = NULL; 273c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott stream_data_len_ = 0; 274c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott return FILTER_NEED_MORE_DATA; 275c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott } 276c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott if (PASS_THROUGH == decoding_status_) { 277c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott // We must pass in available_space, but it will be changed to bytes_used. 278c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott FilterStatus result = CopyOut(dest_buffer, &available_space); 279c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott // Accumulate the returned count of bytes_used (a.k.a., available_space). 280c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott *dest_len += available_space; 281c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott return result; 282c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott } 283c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott DCHECK(false); 284c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott decoding_status_ = DECODING_ERROR; 285c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott return FILTER_ERROR; 286c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott } 287c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 288c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott if (!next_stream_data_ || stream_data_len_ <= 0) 289c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott return FILTER_NEED_MORE_DATA; 290c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 291c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott bool ret = vcdiff_streaming_decoder_->DecodeChunk( 292c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott next_stream_data_, stream_data_len_, &dest_buffer_excess_); 293c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott // Assume all data was used in decoding. 294c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott next_stream_data_ = NULL; 295c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott source_bytes_ += stream_data_len_; 296c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott stream_data_len_ = 0; 297c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott output_bytes_ += dest_buffer_excess_.size(); 298c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott if (!ret) { 299c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott vcdiff_streaming_decoder_.reset(NULL); // Don't call it again. 300c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott decoding_status_ = DECODING_ERROR; 301c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott SdchManager::SdchErrorRecovery(SdchManager::DECODE_BODY_ERROR); 302c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott return FILTER_ERROR; 303c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott } 304c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 305c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott amount = OutputBufferExcess(dest_buffer, available_space); 306c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott *dest_len += amount; 307c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott dest_buffer += amount; 308c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott available_space -= amount; 309c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott if (0 == available_space && !dest_buffer_excess_.empty()) 310c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott return FILTER_OK; 311c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott return FILTER_NEED_MORE_DATA; 312c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott} 313c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 314c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick ScottFilter::FilterStatus SdchFilter::InitializeDictionary() { 315c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott const size_t kServerIdLength = 9; // Dictionary hash plus null from server. 316c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott size_t bytes_needed = kServerIdLength - dictionary_hash_.size(); 317c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott DCHECK_GT(bytes_needed, 0u); 318c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott if (!next_stream_data_) 319c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott return FILTER_NEED_MORE_DATA; 320c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott if (static_cast<size_t>(stream_data_len_) < bytes_needed) { 321c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott dictionary_hash_.append(next_stream_data_, stream_data_len_); 322c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott next_stream_data_ = NULL; 323c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott stream_data_len_ = 0; 324c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott return FILTER_NEED_MORE_DATA; 325c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott } 326c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott dictionary_hash_.append(next_stream_data_, bytes_needed); 327c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott DCHECK(kServerIdLength == dictionary_hash_.size()); 328c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott stream_data_len_ -= bytes_needed; 329c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott DCHECK_LE(0, stream_data_len_); 330c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott if (stream_data_len_ > 0) 331c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott next_stream_data_ += bytes_needed; 332c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott else 333c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott next_stream_data_ = NULL; 334c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 335c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott DCHECK(!dictionary_.get()); 336c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott dictionary_hash_is_plausible_ = true; // Assume plausible, but check. 337c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 338c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott SdchManager::Dictionary* dictionary = NULL; 339c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott if ('\0' == dictionary_hash_[kServerIdLength - 1]) 340c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott SdchManager::Global()->GetVcdiffDictionary(std::string(dictionary_hash_, 0, 341c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott kServerIdLength - 1), 342c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott url_, &dictionary); 343c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott else 344c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott dictionary_hash_is_plausible_ = false; 345c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 346c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott if (!dictionary) { 347c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott DCHECK(dictionary_hash_.size() == kServerIdLength); 348c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott // Since dictionary was not found, check to see if hash was even plausible. 349c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott for (size_t i = 0; i < kServerIdLength - 1; ++i) { 350c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott char base64_char = dictionary_hash_[i]; 351c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott if (!isalnum(base64_char) && '-' != base64_char && '_' != base64_char) { 352c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott dictionary_hash_is_plausible_ = false; 353c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott break; 354c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott } 355c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott } 356c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott if (dictionary_hash_is_plausible_) 357c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott SdchManager::SdchErrorRecovery(SdchManager::DICTIONARY_HASH_NOT_FOUND); 358c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott else 359c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott SdchManager::SdchErrorRecovery(SdchManager::DICTIONARY_HASH_MALFORMED); 360c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott decoding_status_ = DECODING_ERROR; 361c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott return FILTER_ERROR; 362c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott } 363c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott dictionary_ = dictionary; 364c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott vcdiff_streaming_decoder_.reset(new open_vcdiff::VCDiffStreamingDecoder); 365c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott vcdiff_streaming_decoder_->SetAllowVcdTarget(false); 366c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott vcdiff_streaming_decoder_->StartDecoding(dictionary_->text().data(), 367c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott dictionary_->text().size()); 368c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott decoding_status_ = DECODING_IN_PROGRESS; 369c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott return FILTER_OK; 370c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott} 371c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 372c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottint SdchFilter::OutputBufferExcess(char* const dest_buffer, 373c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott size_t available_space) { 374c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott if (dest_buffer_excess_.empty()) 375c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott return 0; 376c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott DCHECK(dest_buffer_excess_.size() > dest_buffer_excess_index_); 377c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott size_t amount = std::min(available_space, 378c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott dest_buffer_excess_.size() - dest_buffer_excess_index_); 379c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott memcpy(dest_buffer, dest_buffer_excess_.data() + dest_buffer_excess_index_, 380c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott amount); 381c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott dest_buffer_excess_index_ += amount; 382c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott if (dest_buffer_excess_.size() <= dest_buffer_excess_index_) { 383c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott DCHECK(dest_buffer_excess_.size() == dest_buffer_excess_index_); 384c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott dest_buffer_excess_.clear(); 385c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott dest_buffer_excess_index_ = 0; 386c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott } 387c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott return amount; 388c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott} 38972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 39072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen} // namespace net 391