Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(97)

Unified Diff: net/filter/sdch_source_stream.cc

Issue 2368433002: Add net::SdchSourceStream and net::SdchPolicyDelegate (Closed)
Patch Set: tidy up tests Created 4 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: net/filter/sdch_source_stream.cc
diff --git a/net/filter/sdch_source_stream.cc b/net/filter/sdch_source_stream.cc
new file mode 100644
index 0000000000000000000000000000000000000000..24684ba5db947b5f7b7f0b1f397d4669c626970a
--- /dev/null
+++ b/net/filter/sdch_source_stream.cc
@@ -0,0 +1,186 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/filter/sdch_source_stream.h"
+
+#include "base/auto_reset.h"
+#include "base/bind.h"
+#include "base/logging.h"
+#include "base/numerics/safe_conversions.h"
+#include "net/base/io_buffer.h"
+#include "sdch/open-vcdiff/src/google/vcdecoder.h"
+
+namespace net {
+
+namespace {
+
+const size_t kServerIdLength = 9;
+const char kSDCH[] = "SDCH";
+
+} // namespace
+
+SdchSourceStream::SdchSourceStream(std::unique_ptr<SourceStream> previous,
+ Delegate* delegate)
+ : FilterSourceStream(SourceStream::TYPE_SDCH, std::move(previous)),
+ delegate_(delegate),
+ output_replaced_(false),
+ passthrough_(false),
+ input_state_(STATE_LOAD_DICTIONARY) {
+ CHECK(delegate);
+}
+
+SdchSourceStream::~SdchSourceStream() {}
+
+void SdchSourceStream::StopDecoding() {
+ DCHECK(in_delegate_handler_);
+ passthrough_ = true;
Randy Smith (Not in Mondays) 2016/09/27 20:09:13 For reasons we've sorta explored in the gzip CL, I
xunjieli 2016/09/30 15:32:17 Done. The pass-through case is a bit tricky. If th
+}
+
+void SdchSourceStream::ReplaceOutput(const char* data, size_t size) {
+ DCHECK(in_delegate_handler_);
+ output_replaced_ = true;
+ buffered_output_.assign(data, size);
+}
+
+std::string SdchSourceStream::GetTypeAsString() const {
+ return kSDCH;
+}
+
+int SdchSourceStream::FilterData(IOBuffer* output_buffer,
+ int output_buffer_size,
+ IOBuffer* input_buffer,
+ int input_buffer_size,
+ int* consumed_bytes,
+ bool /*upstream_end_reached*/) {
+ DCHECK_LE(0, input_buffer_size);
+ size_t input_data_size = input_buffer_size;
+ char* input_data = input_buffer->data();
+ int bytes_out = 0;
+ while (true) {
+ InputState state = input_state_;
Randy Smith (Not in Mondays) 2016/09/27 20:09:13 Why the separate variable? It seems like it's onl
xunjieli 2016/09/30 15:32:18 Done.
+ switch (state) {
Randy Smith (Not in Mondays) 2016/09/27 20:09:13 nit, suggestion: Order these states (here and in t
xunjieli 2016/09/30 15:32:18 Done.
+ case STATE_LOAD_DICTIONARY: {
+ // Copy at most |kServerIdLength| from |input_buffer|.
+ size_t to_copy = std::min(kServerIdLength - dictionary_id_.length(),
+ input_data_size);
+ dictionary_id_.append(input_data, to_copy);
+ input_data_size -= to_copy;
+ input_data += to_copy;
+
+ // Not enough bytes for a dictionary ID accumulated yet.
+ if (dictionary_id_.length() != kServerIdLength) {
+ *consumed_bytes = input_buffer_size - input_data_size;
+ return 0;
+ }
+ // To avoid passing a std::string with a null terminator into
+ // GetDictionary(), |server_hash| here removes the last byte blindly,
+ // and this method only calls GetDictionary() with it if
+ // CouldBeDictionaryId() returns true.
+ std::string server_hash = dictionary_id_.substr(0, kServerIdLength - 1);
Randy Smith (Not in Mondays) 2016/09/27 20:09:13 nit, suggestion: server_hash only appears to be us
xunjieli 2016/09/30 15:32:18 Done.
+ const std::string* dictionary_text = nullptr;
+ if (!CouldBeDictionaryId(dictionary_id_) ||
+ !delegate_->OnGetDictionary(server_hash, &dictionary_text)) {
+ input_data_size = 0;
+ buffered_output_.append(dictionary_id_);
+ bool handled = AskDelegateToHandleDictionaryError();
+ if (!handled)
+ return ERR_CONTENT_DECODING_FAILED;
+ input_state_ = STATE_HANDLE_ERROR;
+ break;
+ }
+ decoder_.reset(new open_vcdiff::VCDiffStreamingDecoder);
+ decoder_->SetAllowVcdTarget(false);
+ decoder_->StartDecoding(dictionary_text->data(),
+ dictionary_text->length());
+ input_state_ = STATE_DECODE;
+ break;
+ }
+ case STATE_FLUSH_INTERNAL_BUFFER: {
+ size_t to_copy =
+ std::min(base::checked_cast<size_t>(output_buffer_size - bytes_out),
+ buffered_output_.length());
+ memcpy(output_buffer->data() + bytes_out, buffered_output_.data(),
+ to_copy);
+ buffered_output_.erase(0, to_copy);
+ bytes_out += to_copy;
+ if (!buffered_output_.empty()) {
+ *consumed_bytes = input_buffer_size - input_data_size;
+ return bytes_out;
+ }
+ input_state_ = STATE_DECODE;
Randy Smith (Not in Mondays) 2016/09/27 20:09:13 Sorry, I'm really confused. Under what circumstan
xunjieli 2016/09/30 15:32:17 Done.FLUSH_INTERNAL_BUFFER is not an error state.
+ break;
+ }
+ case STATE_PASS_THROUGH: {
+ DCHECK(buffered_output_.empty());
+ size_t to_copy =
+ std::min(base::checked_cast<size_t>(output_buffer_size - bytes_out),
+ input_data_size);
+ memcpy(output_buffer->data() + bytes_out, input_data, to_copy);
+ input_data += to_copy;
+ input_data_size -= to_copy;
+ *consumed_bytes = input_buffer_size - input_data_size;
+ return bytes_out;
+ }
+ case STATE_HANDLE_ERROR: {
+ DCHECK(passthrough_ || output_replaced_);
+ DCHECK(!(passthrough_ && output_replaced_));
+ if (passthrough_) {
+ input_state_ = STATE_PASS_THROUGH;
+ }
+ if (output_replaced_) {
+ input_state_ = STATE_FLUSH_INTERNAL_BUFFER;
+ }
+ break;
+ }
+ case STATE_DECODE: {
+ DCHECK(buffered_output_.empty());
+ if (input_data_size == 0) {
+ *consumed_bytes = input_buffer_size - input_data_size;
+ return bytes_out;
+ }
Randy Smith (Not in Mondays) 2016/09/27 20:09:13 nit: It "feels" like this test is generic, not spe
xunjieli 2016/09/30 15:32:18 Done.
+ bool ok = decoder_->DecodeChunk(input_data, input_data_size,
+ &buffered_output_);
+ // Calls to DecodeChunk always consume all their input, so this always
+ // drains the entire buffer.
+ input_data += input_data_size;
+ input_data_size = 0;
+ if (!ok) {
+ decoder_.reset();
+ bool handled = AskDelegateToHandleDecodingError();
+ if (!handled)
+ return ERR_CONTENT_DECODING_FAILED;
+ input_state_ = STATE_HANDLE_ERROR;
+ break;
+ }
+ input_state_ = STATE_FLUSH_INTERNAL_BUFFER;
+ break;
+ }
+ }
+ }
+ NOTREACHED();
+ return ERR_UNEXPECTED;
+}
+
+bool SdchSourceStream::CouldBeDictionaryId(const std::string& id) const {
+ for (size_t i = 0; i < kServerIdLength - 1; i++) {
+ char base64_char = id[i];
+ if (!isalnum(base64_char) && '-' != base64_char && '_' != base64_char)
+ return false;
+ }
+ if (id[kServerIdLength - 1] != '\0')
+ return false;
+ return true;
+}
+
+bool SdchSourceStream::AskDelegateToHandleDictionaryError() {
+ base::AutoReset<bool> resetter(&in_delegate_handler_, true);
+ return delegate_->OnDictionaryError(this);
+}
+
+bool SdchSourceStream::AskDelegateToHandleDecodingError() {
+ base::AutoReset<bool> resetter(&in_delegate_handler_, true);
+ return delegate_->OnDecodingError(this);
+}
+
+} // namespace net

Powered by Google App Engine
This is Rietveld 408576698