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