Chromium Code Reviews| 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 |