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

Unified Diff: net/filter/stream_source.cc

Issue 1662763002: [ON HOLD] Implement pull-based design for content decoding (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Refactor common logic Created 4 years, 10 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/stream_source.cc
diff --git a/net/filter/stream_source.cc b/net/filter/stream_source.cc
new file mode 100644
index 0000000000000000000000000000000000000000..a70361babfde97e1febb4851211ad54a8a1a024d
--- /dev/null
+++ b/net/filter/stream_source.cc
@@ -0,0 +1,222 @@
+// 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/stream_source.h"
+
+#include "base/bind.h"
+#include "base/callback_helpers.h"
+#include "base/strings/string_util.h"
+#include "net/filter/block_buffer.h"
+#include "net/filter/brotli_stream_source.h"
+#include "net/filter/gzip_stream_source.h"
+#include "net/filter/sdch_stream_source.h"
+
+namespace net {
+
+namespace {
+
+const char kDeflate[] = "deflate";
+const char kGZip[] = "gzip";
+const char kSdch[] = "sdch";
+const char kXGZip[] = "x-gzip";
+const char kBrotli[] = "br";
+
+std::vector<StreamSource::SourceType> SourceTypeNamesToTypes(
+ const std::vector<std::string>& types) {
+ std::vector<StreamSource::SourceType> result;
+ for (const auto& type : types) {
+ if (base::LowerCaseEqualsASCII(type, kBrotli)) {
+ result.push_back(StreamSource::SOURCE_BROTLI);
+ } else if (base::LowerCaseEqualsASCII(type, kDeflate)) {
+ result.push_back(StreamSource::SOURCE_DEFLATE);
+ } else if (base::LowerCaseEqualsASCII(type, kGZip) ||
+ base::LowerCaseEqualsASCII(type, kXGZip)) {
+ result.push_back(StreamSource::SOURCE_GZIP);
+ } else if (base::LowerCaseEqualsASCII(type, kSdch)) {
+ result.push_back(StreamSource::SOURCE_SDCH);
+ }
+ }
mmenke 2016/03/04 21:15:57 If we see an unrecognized scheme, we should just g
Randy Smith (Not in Mondays) 2016/03/09 23:03:56 Define "give up"? The current behavior (which I d
mmenke 2016/03/09 23:11:50 That's what I meant by "give up". :)
xunjieli 2016/04/20 19:16:10 Acknowledged.
+ return result;
+}
+
+std::string SourceTypeAsString(StreamSource::SourceType type) {
+ switch (type) {
+ case StreamSource::SOURCE_BROTLI:
mmenke 2016/03/04 21:15:57 We presumably won't change these often, but I sugg
xunjieli 2016/04/20 19:16:09 Done.
+ return "SOURCE_TYPE_BROTLI";
+ case StreamSource::SOURCE_DEFLATE:
+ return "SOURCE_TYPE_DEFLATE";
+ case StreamSource::SOURCE_GZIP:
+ return "SOURCE_TYPE_GZIP";
+ case StreamSource::SOURCE_SDCH:
+ return "SOURCE_TYPE_SDCH";
+ case StreamSource::SOURCE_GZIP_FALLBACK:
+ return "SOURCE_TYPE_GZIP_FALLBACK";
+ case StreamSource::SOURCE_INVALID:
+ return "SOURCE_TYPE_INVALID";
+ case StreamSource::SOURCE_NONE:
+ return "SOURCE_TYPE_NONE";
+ }
mmenke 2016/03/04 21:15:56 NOTREACHED()?
xunjieli 2016/04/20 19:16:09 Done.
+ return "";
+}
+
+scoped_ptr<StreamSource> BuildSource(scoped_ptr<StreamSource> current,
mmenke 2016/03/04 21:15:57 Randy may have an opinion here, but I think it's a
Randy Smith (Not in Mondays) 2016/03/09 23:03:56 This matches the (IMO bad for the reasons you arti
xunjieli 2016/04/20 19:16:10 Done. I moved them to a util file.
+ StreamSource::SourceType type,
+ SdchStreamSourceDelegate* delegate) {
+ if (type == StreamSource::SOURCE_BROTLI) {
+ scoped_ptr<BrotliStreamSource> next(
+ new BrotliStreamSource(std::move(current)));
+ current = std::move(next);
mmenke 2016/03/04 21:15:57 overwriting current with next seems weird. Sugges
xunjieli 2016/04/20 19:16:10 Done.
+ } else if (type == StreamSource::SOURCE_SDCH) {
+ scoped_ptr<SdchStreamSource> next(
+ new SdchStreamSource(std::move(current), delegate));
+ if (next->Init())
+ current = std::move(next);
mmenke 2016/03/04 21:15:57 On failure, just return NULL? I'd also like to se
xunjieli 2016/04/20 19:16:09 Partially DONE. How do we make SDCH's init() fail?
+ } else if (type == StreamSource::SOURCE_GZIP ||
+ type == StreamSource::SOURCE_DEFLATE ||
+ type == StreamSource::SOURCE_GZIP_FALLBACK) {
+ scoped_ptr<GzipStreamSource> next(new GzipStreamSource(std::move(current)));
+ GzipStreamSource::GzipStreamSourceMode mode =
+ type == StreamSource::SOURCE_DEFLATE
+ ? GzipStreamSource::GZIP_STREAM_SOURCE_DEFLATE
+ : GzipStreamSource::GZIP_STREAM_SOURCE_GZIP;
+ bool fallback = type == StreamSource::SOURCE_GZIP_FALLBACK;
+ if (next->Init(mode, fallback))
+ current = std::move(next);
mmenke 2016/03/04 21:15:56 On failure, just return NULL?
xunjieli 2016/04/20 19:16:09 Done.
+ }
+ return current;
+}
+
+} // namespace
+
+StreamSource::StreamSource(SourceType type, scoped_ptr<StreamSource> previous)
+ : type_(type), previous_(std::move(previous)) {
+ // Initializes |buffer_| if only the stream source needs to read input data
+ // from |previous_|.
+ if (previous_)
+ buffer_.reset(new BlockBuffer());
Randy Smith (Not in Mondays) 2016/03/09 23:03:56 Could get rid of this magic here by allocating it
xunjieli 2016/04/20 19:16:10 Done.
+}
+
+StreamSource::~StreamSource() {}
+
+Error StreamSource::Read(IOBuffer* dest_buffer,
+ size_t buffer_size,
+ size_t* bytes_read,
+ const OnReadCompleteCallback& callback) {
+ *bytes_read = 0;
+
+ Error error = OK;
+ while (error == OK) {
+ size_t single_bytes_read = 0;
+ error = ReadInternal(dest_buffer, buffer_size, &single_bytes_read);
+ *bytes_read += single_bytes_read;
+
+ // ReadInternal() returns synchronously or an error occurred, return right
+ // here.
+ if ((error == OK && single_bytes_read > 0) ||
+ (error != OK && error != ERR_IO_PENDING))
Randy Smith (Not in Mondays) 2016/03/09 23:03:56 Is ReadInternal *allowed* to return ERR_IO_PENDING
xunjieli 2016/04/20 19:16:09 Done.
+ return error;
+
+ // Needs more input, it has consumed all existing input.
+ DCHECK(!buffer_ || !buffer_->HasMoreBytes());
+
+ if (!previous_)
+ break;
mmenke 2016/03/04 21:15:57 This previous_ magic (And the magic in the constru
Randy Smith (Not in Mondays) 2016/03/09 23:03:56 Based on my current understanding, I'd prefer the
xunjieli 2016/04/20 19:16:09 Done.
xunjieli 2016/04/20 19:16:09 Done.
+
+ // Dispatch a read to refill the input buffer.
+ size_t previous_bytes_read = 0;
+ error = previous_->Read(
+ buffer_->buffer(), buffer_->size(), &previous_bytes_read,
+ base::Bind(&StreamSource::OnReadComplete, base::Unretained(this),
Randy Smith (Not in Mondays) 2016/03/09 23:03:56 nit, knee-jerk response: Use of Unretained should
xunjieli 2016/04/20 19:16:10 Done.
+ base::Unretained(dest_buffer), buffer_size));
+
+ // OK with 0 bytes read means EOF. Since the buffer is already empty, and
+ // Decompress already failed to return any more data, this source is also
Randy Smith (Not in Mondays) 2016/03/09 23:03:56 nit: I think you mean ReadInternal, not Decompress
xunjieli 2016/04/20 19:16:10 Done.
+ // at EOF. Just return that synchronously.
+ if (error == OK && previous_bytes_read == 0)
+ return OK;
+
+ // If the underlying read completed synchronously, mark the buffer as
+ // refilled and try again.
+ if (error == OK)
+ buffer_->WasRefilled(previous_bytes_read);
+ }
+
+ DCHECK(!buffer_ || !buffer_->HasMoreBytes());
+
+ if (error == ERR_IO_PENDING) {
+ callback_ = callback;
+ pending_read_buffer_ = dest_buffer;
mmenke 2016/03/04 21:15:57 Hrm...seems a little weird to set these when this
Randy Smith (Not in Mondays) 2016/03/09 23:03:56 No, I think I agree. I'd vote for making the call
xunjieli 2016/04/20 19:16:10 Done.
xunjieli 2016/04/20 19:16:10 Done.
+ }
+ return error;
+}
+
+void StreamSource::OnReadComplete(IOBuffer* dest_buffer,
+ size_t dest_buffer_size,
+ Error error,
+ size_t bytes_read) {
+ DCHECK(!buffer_ || !buffer_->HasMoreBytes());
+ DCHECK_EQ(dest_buffer, pending_read_buffer_.get());
+ DCHECK(!callback_.is_null());
+
+ // Take a ref for the lifetime of this function.
+ scoped_refptr<IOBuffer> dest_ref(dest_buffer);
+ pending_read_buffer_ = nullptr;
+
+ // If the underlying read failed, fail this read directly.
+ if (error != OK) {
+ base::ResetAndReturn(&callback_).Run(error, bytes_read);
+ return;
+ }
+
+ if (bytes_read == 0) {
+ // EOF. Since the buffer is empty, there is no more data to decompress (any
+ // internally buffered data would have been drained already before calling
+ // the previous stream's Read). Return EOF to our caller.
+ if (!callback_.is_null()) {
mmenke 2016/03/04 21:15:56 Why check this here, but not in the "error != OK"
xunjieli 2016/04/20 19:16:10 Done.
+ base::ResetAndReturn(&callback_).Run(error, bytes_read);
+ }
+ return;
+ }
+
+ // Mark the buffer as refilled and try decompressing.
+ buffer_->WasRefilled(bytes_read);
+
+ // Recurse. Read runs the callback if completes synchronously,
Randy Smith (Not in Mondays) 2016/03/09 23:03:56 I think this comment is wrong? The code is callin
xunjieli 2016/04/20 19:16:10 Done.
+ // Otherwise, Read will have posted an asynchronous read that
+ // will later re-invoke OnReadComplete to run the callback.
+ error = Read(dest_buffer, dest_buffer_size, &bytes_read, callback_);
+ if (error != ERR_IO_PENDING)
+ base::ResetAndReturn(&callback_).Run(error, bytes_read);
+}
+
+scoped_ptr<StreamSource> StreamSource::BuildSourceChain(
+ scoped_ptr<StreamSource> current,
+ const std::vector<std::string>& type_names,
+ SdchStreamSourceDelegate* sdch_delegate) {
mmenke 2016/03/04 21:15:56 Suggest just passing in the headers, and walking t
xunjieli 2016/04/20 19:16:10 Done.
+ std::vector<SourceType> types = SourceTypeNamesToTypes(type_names);
+
+ // SDCH-specific hack: if the first filter is SDCH, add a gzip filter in front
+ // of it in fallback mode.
+ if (!types.empty() && types.at(0) == SOURCE_SDCH)
+ types.insert(types.begin(), SOURCE_GZIP_FALLBACK);
+
+ for (const auto& type : types) {
+ current = BuildSource(std::move(current), type, sdch_delegate);
+ if (current == nullptr)
+ return nullptr;
+ }
+
+ return current;
+}
+
+std::string StreamSource::OrderedStreamSourceList() const {
+ if (previous_) {
+ return SourceTypeAsString(type_) + "," +
+ previous_->OrderedStreamSourceList();
+ } else {
+ return SourceTypeAsString(type_);
+ }
+}
+
+} // namespace net

Powered by Google App Engine
This is Rietveld 408576698