| OLD | NEW | 
|---|
| (Empty) |  | 
|  | 1 // Copyright 2016 The Chromium Authors. All rights reserved. | 
|  | 2 // Use of this source code is governed by a BSD-style license that can be | 
|  | 3 // found in the LICENSE file. | 
|  | 4 | 
|  | 5 #include "net/filter/sdch_stream_source.h" | 
|  | 6 | 
|  | 7 #include "base/auto_reset.h" | 
|  | 8 #include "base/bind.h" | 
|  | 9 #include "net/filter/block_buffer.h" | 
|  | 10 #include "sdch/open-vcdiff/src/google/vcdecoder.h" | 
|  | 11 | 
|  | 12 namespace net { | 
|  | 13 | 
|  | 14 namespace { | 
|  | 15 | 
|  | 16 const size_t kServerIdLength = 9; | 
|  | 17 | 
|  | 18 }  // namespace | 
|  | 19 | 
|  | 20 SdchStreamSource::SdchStreamSource(std::unique_ptr<StreamSource> previous, | 
|  | 21                                    SdchStreamSourceDelegate* delegate) | 
|  | 22     : FilterStreamSource(StreamSource::TYPE_SDCH, std::move(previous)), | 
|  | 23       decoder_(new open_vcdiff::VCDiffStreamingDecoder), | 
|  | 24       delegate_(delegate), | 
|  | 25       output_replaced_(false), | 
|  | 26       passthrough_(false), | 
|  | 27       dictionary_tried_load_(false) {} | 
|  | 28 | 
|  | 29 SdchStreamSource::~SdchStreamSource() { | 
|  | 30   // In unit tests, delegate_ may be null. | 
|  | 31   if (delegate_) | 
|  | 32     delegate_->NotifyStreamDone(); | 
|  | 33 } | 
|  | 34 | 
|  | 35 bool SdchStreamSource::Init() { | 
|  | 36   return true; | 
|  | 37 } | 
|  | 38 | 
|  | 39 void SdchStreamSource::StopDecoding() { | 
|  | 40   DCHECK(in_delegate_handler_); | 
|  | 41   passthrough_ = true; | 
|  | 42 } | 
|  | 43 | 
|  | 44 void SdchStreamSource::ReplaceOutput(const char* data, size_t size) { | 
|  | 45   DCHECK(in_delegate_handler_); | 
|  | 46   buffered_output_.assign(data, size); | 
|  | 47 } | 
|  | 48 | 
|  | 49 Error SdchStreamSource::ReadInternal(IOBuffer* dest_buffer, | 
|  | 50                                      size_t buffer_size, | 
|  | 51                                      size_t* bytes_read) { | 
|  | 52   // If there is no loaded dictionary, try loading a dictionary ID from | 
|  | 53   // |buffer_| and then loading a dictionary. If anything goes wrong in | 
|  | 54   // LoadDictionary, the delegate's HandleDictionaryError() call is expected | 
|  | 55   // to recover, either by calling StopDecoding() or ReplaceOutput(). Both of | 
|  | 56   // these are handled by Decompress (as is having no dictionary loaded). If | 
|  | 57   // the delegate's HandleDictionaryError cannot recover, this function | 
|  | 58   // returns a read error. | 
|  | 59   if (!dictionary_tried_load_ && !LoadDictionary()) | 
|  | 60     return ERR_FAILED; | 
|  | 61 | 
|  | 62   // Run the synchronous decompressor. | 
|  | 63   SdchStreamState state = Decompress(dest_buffer, buffer_size, bytes_read); | 
|  | 64 | 
|  | 65   // If decompression failed, call HandleDecodingError on the delegate, then | 
|  | 66   // rerun the decompressor. The decompressor shouldn't actually decompress | 
|  | 67   // this time, since the delegate should have called either Passthrough or | 
|  | 68   // ReplaceOutput. | 
|  | 69   if (state == SDCH_STREAM_ERROR) { | 
|  | 70     bool handled = AskDelegateToHandleDecodingError(); | 
|  | 71     if (!handled) | 
|  | 72       return ERR_FAILED; | 
|  | 73     state = Decompress(dest_buffer, buffer_size, bytes_read); | 
|  | 74   } | 
|  | 75 | 
|  | 76   if (*bytes_read > 0) { | 
|  | 77     return OK; | 
|  | 78   } | 
|  | 79 | 
|  | 80   DCHECK(state == SDCH_STREAM_MORE_INPUT); | 
|  | 81   DCHECK(!buffer_->HasMoreBytes()); | 
|  | 82 | 
|  | 83   // If someone called ReplaceOutput to replace this stream's output, | 
|  | 84   // Decompress will return all the buffered output; if it did not do so, this | 
|  | 85   // stream is at EOF. | 
|  | 86   if (output_replaced_) | 
|  | 87     return OK; | 
|  | 88 | 
|  | 89   return OK; | 
|  | 90 } | 
|  | 91 | 
|  | 92 // Synchronous decompressor core. | 
|  | 93 SdchStreamSource::SdchStreamState SdchStreamSource::Decompress( | 
|  | 94     IOBuffer* dest_buffer, | 
|  | 95     size_t buffer_size, | 
|  | 96     size_t* bytes_read) { | 
|  | 97   // If output has been replaced via ReplaceOutput, short-circuit any other | 
|  | 98   // processing, and emit as much of the buffered output as possible. If there's | 
|  | 99   // no buffered output left, return EOF. | 
|  | 100   if (output_replaced_) | 
|  | 101     return Flush(dest_buffer, buffer_size, bytes_read); | 
|  | 102 | 
|  | 103   // If this stream is no longer decoding, pass any input through. | 
|  | 104   if (passthrough_) | 
|  | 105     return Passthrough(dest_buffer, buffer_size, bytes_read); | 
|  | 106 | 
|  | 107   // Can't do any decoding until a dictionary is loaded, which requires more | 
|  | 108   // input. | 
|  | 109   if (!dictionary_tried_load_) | 
|  | 110     return SDCH_STREAM_MORE_INPUT; | 
|  | 111 | 
|  | 112   // If there's any data left in the output buffer, try flushing some of it. | 
|  | 113   if (Flush(dest_buffer, buffer_size, bytes_read) != SDCH_STREAM_MORE_INPUT) | 
|  | 114     return SDCH_STREAM_MORE_OUTPUT_SPACE; | 
|  | 115 | 
|  | 116   // This is awkward. In principle, this function is called only when the input | 
|  | 117   // buffer has more bytes, since vcdiff always consumes input; unfortunately, | 
|  | 118   // the dictionary-loading code earlier in the Read() loop can consume all the | 
|  | 119   // currently buffered input. If that happened, Decompress returns early here. | 
|  | 120   if (!buffer_->HasMoreBytes()) | 
|  | 121     return SDCH_STREAM_MORE_INPUT; | 
|  | 122 | 
|  | 123   // Now there is no data left in the output buffer and there is data in the | 
|  | 124   // input buffer, so run the vcdiff decoder. | 
|  | 125   DCHECK(buffered_output_.empty()); | 
|  | 126   DCHECK(buffer_->HasMoreBytes()); | 
|  | 127 | 
|  | 128   // Calls to DecodeChunk always consume all their input, so this always drains | 
|  | 129   // the entire buffer. | 
|  | 130   bool ok = decoder_->DecodeChunk(buffer_->bytes(), buffer_->bytes_left(), | 
|  | 131                                   &buffered_output_); | 
|  | 132   if (ok) { | 
|  | 133     buffer_->WasDrained(buffer_->bytes_left()); | 
|  | 134   } else { | 
|  | 135     if (!AskDelegateToHandleDecodingError()) | 
|  | 136       return SDCH_STREAM_ERROR; | 
|  | 137   } | 
|  | 138 | 
|  | 139   return Flush(dest_buffer, buffer_size, bytes_read); | 
|  | 140 } | 
|  | 141 | 
|  | 142 SdchStreamSource::SdchStreamState SdchStreamSource::Passthrough( | 
|  | 143     IOBuffer* dest_buffer, | 
|  | 144     size_t buffer_size, | 
|  | 145     size_t* bytes_read) { | 
|  | 146   SdchStreamState state = Flush(dest_buffer, buffer_size, bytes_read); | 
|  | 147   if (state == SDCH_STREAM_MORE_OUTPUT_SPACE) | 
|  | 148     return state; | 
|  | 149 | 
|  | 150   // No buffered data to flush. See if there's any input. | 
|  | 151   DCHECK(buffered_output_.empty()); | 
|  | 152   if (!buffer_->HasMoreBytes()) | 
|  | 153     return SDCH_STREAM_MORE_INPUT; | 
|  | 154 | 
|  | 155   size_t to_copy = buffer_size; | 
|  | 156   if (to_copy > buffer_->bytes_left()) | 
|  | 157     to_copy = buffer_->bytes_left(); | 
|  | 158   memcpy(dest_buffer->data(), buffer_->bytes(), to_copy); | 
|  | 159   buffer_->WasDrained(to_copy); | 
|  | 160   if (buffer_->HasMoreBytes()) | 
|  | 161     return SDCH_STREAM_MORE_OUTPUT_SPACE; | 
|  | 162   else | 
|  | 163     return SDCH_STREAM_MORE_INPUT; | 
|  | 164 } | 
|  | 165 | 
|  | 166 SdchStreamSource::SdchStreamState SdchStreamSource::Flush(IOBuffer* dest_buffer, | 
|  | 167                                                           size_t buffer_size, | 
|  | 168                                                           size_t* bytes_read) { | 
|  | 169   size_t to_copy = buffer_size; | 
|  | 170   if (to_copy > buffered_output_.length()) | 
|  | 171     to_copy = buffered_output_.length(); | 
|  | 172   if (to_copy == 0) | 
|  | 173     return SDCH_STREAM_MORE_INPUT; | 
|  | 174   memcpy(dest_buffer->data(), buffered_output_.data(), to_copy); | 
|  | 175   buffered_output_.erase(0, to_copy); | 
|  | 176   *bytes_read = to_copy; | 
|  | 177   return SDCH_STREAM_MORE_OUTPUT_SPACE; | 
|  | 178 } | 
|  | 179 | 
|  | 180 // Attempts to find a dictionary ID at the start of the input stream and load | 
|  | 181 // the dictionary with that identifier. Returns true if no error happened or an | 
|  | 182 // error happened but the delegate repaired it, and false if an error happened | 
|  | 183 // and the delegate did not repair it. | 
|  | 184 bool SdchStreamSource::LoadDictionary() { | 
|  | 185   size_t to_copy = kServerIdLength - dictionary_id_.length(); | 
|  | 186   const std::string* dictionary_text = nullptr; | 
|  | 187   if (to_copy > buffer_->bytes_left()) | 
|  | 188     to_copy = buffer_->bytes_left(); | 
|  | 189   dictionary_id_.append(buffer_->bytes(), to_copy); | 
|  | 190   buffer_->WasDrained(to_copy); | 
|  | 191 | 
|  | 192   // Not enough bytes for a dictionary ID accumulated yet. | 
|  | 193   if (dictionary_id_.length() != kServerIdLength) | 
|  | 194     return true; | 
|  | 195 | 
|  | 196   dictionary_tried_load_ = true; | 
|  | 197 | 
|  | 198   // If the dictionary ID looks bogus, or the delegate can't find a dictionary | 
|  | 199   // with that identifier, then call HandleDictionaryError and bail. | 
|  | 200   // Also, stick the dictionary ID into the output buffer here; LoadDictionary | 
|  | 201   // will consume it assuming it's a dictionary ID, so if it's really not, the | 
|  | 202   // "dictionary ID" should appear in the output stream. | 
|  | 203   // | 
|  | 204   // To avoid passing a std::string with a null terminator into GetDictionary(), | 
|  | 205   // |stem_id| here removes the last byte blindly, and this method only calls | 
|  | 206   // GetDictionary with it if CouldBeDictionaryId returns true. | 
|  | 207   std::string stem_id = dictionary_id_.substr(0, kServerIdLength - 1); | 
|  | 208   if (!CouldBeDictionaryId(dictionary_id_) || | 
|  | 209       !delegate_->GetDictionary(stem_id, &dictionary_text)) { | 
|  | 210     buffered_output_.append(dictionary_id_); | 
|  | 211     return AskDelegateToHandleDictionaryError(); | 
|  | 212   } | 
|  | 213 | 
|  | 214   decoder_->StartDecoding(dictionary_text->data(), dictionary_text->length()); | 
|  | 215   return true; | 
|  | 216 } | 
|  | 217 | 
|  | 218 bool SdchStreamSource::CouldBeDictionaryId(const std::string& id) const { | 
|  | 219   std::string kBase64UrlChars = | 
|  | 220       "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-"; | 
|  | 221   for (size_t i = 0; i < kServerIdLength - 1; i++) { | 
|  | 222     if (kBase64UrlChars.find(id[i]) == std::string::npos) | 
|  | 223       return false; | 
|  | 224   } | 
|  | 225   if (id[kServerIdLength - 1] != '\0') | 
|  | 226     return false; | 
|  | 227   return true; | 
|  | 228 } | 
|  | 229 | 
|  | 230 bool SdchStreamSource::AskDelegateToHandleDictionaryError() { | 
|  | 231   base::AutoReset<bool> resetter(&in_delegate_handler_, true); | 
|  | 232   return delegate_->HandleDictionaryError(this); | 
|  | 233 } | 
|  | 234 | 
|  | 235 bool SdchStreamSource::AskDelegateToHandleDecodingError() { | 
|  | 236   base::AutoReset<bool> resetter(&in_delegate_handler_, true); | 
|  | 237   return delegate_->HandleDecodingError(this); | 
|  | 238 } | 
|  | 239 | 
|  | 240 }  // namespace net | 
| OLD | NEW | 
|---|