Chromium Code Reviews| 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/stream_source.h" | |
| 6 | |
| 7 #include "base/bind.h" | |
| 8 #include "base/callback_helpers.h" | |
| 9 #include "base/strings/string_util.h" | |
| 10 #include "net/filter/block_buffer.h" | |
| 11 #include "net/filter/brotli_stream_source.h" | |
| 12 #include "net/filter/gzip_stream_source.h" | |
| 13 #include "net/filter/sdch_stream_source.h" | |
| 14 | |
| 15 namespace net { | |
| 16 | |
| 17 namespace { | |
| 18 | |
| 19 const char kDeflate[] = "deflate"; | |
| 20 const char kGZip[] = "gzip"; | |
| 21 const char kSdch[] = "sdch"; | |
| 22 const char kXGZip[] = "x-gzip"; | |
| 23 const char kBrotli[] = "br"; | |
| 24 | |
| 25 std::vector<StreamSource::SourceType> SourceTypeNamesToTypes( | |
| 26 const std::vector<std::string>& types) { | |
| 27 std::vector<StreamSource::SourceType> result; | |
| 28 for (const auto& type : types) { | |
| 29 if (base::LowerCaseEqualsASCII(type, kBrotli)) { | |
| 30 result.push_back(StreamSource::SOURCE_BROTLI); | |
| 31 } else if (base::LowerCaseEqualsASCII(type, kDeflate)) { | |
| 32 result.push_back(StreamSource::SOURCE_DEFLATE); | |
| 33 } else if (base::LowerCaseEqualsASCII(type, kGZip) || | |
| 34 base::LowerCaseEqualsASCII(type, kXGZip)) { | |
| 35 result.push_back(StreamSource::SOURCE_GZIP); | |
| 36 } else if (base::LowerCaseEqualsASCII(type, kSdch)) { | |
| 37 result.push_back(StreamSource::SOURCE_SDCH); | |
| 38 } | |
| 39 } | |
|
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.
| |
| 40 return result; | |
| 41 } | |
| 42 | |
| 43 std::string SourceTypeAsString(StreamSource::SourceType type) { | |
| 44 switch (type) { | |
| 45 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.
| |
| 46 return "SOURCE_TYPE_BROTLI"; | |
| 47 case StreamSource::SOURCE_DEFLATE: | |
| 48 return "SOURCE_TYPE_DEFLATE"; | |
| 49 case StreamSource::SOURCE_GZIP: | |
| 50 return "SOURCE_TYPE_GZIP"; | |
| 51 case StreamSource::SOURCE_SDCH: | |
| 52 return "SOURCE_TYPE_SDCH"; | |
| 53 case StreamSource::SOURCE_GZIP_FALLBACK: | |
| 54 return "SOURCE_TYPE_GZIP_FALLBACK"; | |
| 55 case StreamSource::SOURCE_INVALID: | |
| 56 return "SOURCE_TYPE_INVALID"; | |
| 57 case StreamSource::SOURCE_NONE: | |
| 58 return "SOURCE_TYPE_NONE"; | |
| 59 } | |
|
mmenke
2016/03/04 21:15:56
NOTREACHED()?
xunjieli
2016/04/20 19:16:09
Done.
| |
| 60 return ""; | |
| 61 } | |
| 62 | |
| 63 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.
| |
| 64 StreamSource::SourceType type, | |
| 65 SdchStreamSourceDelegate* delegate) { | |
| 66 if (type == StreamSource::SOURCE_BROTLI) { | |
| 67 scoped_ptr<BrotliStreamSource> next( | |
| 68 new BrotliStreamSource(std::move(current))); | |
| 69 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.
| |
| 70 } else if (type == StreamSource::SOURCE_SDCH) { | |
| 71 scoped_ptr<SdchStreamSource> next( | |
| 72 new SdchStreamSource(std::move(current), delegate)); | |
| 73 if (next->Init()) | |
| 74 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?
| |
| 75 } else if (type == StreamSource::SOURCE_GZIP || | |
| 76 type == StreamSource::SOURCE_DEFLATE || | |
| 77 type == StreamSource::SOURCE_GZIP_FALLBACK) { | |
| 78 scoped_ptr<GzipStreamSource> next(new GzipStreamSource(std::move(current))); | |
| 79 GzipStreamSource::GzipStreamSourceMode mode = | |
| 80 type == StreamSource::SOURCE_DEFLATE | |
| 81 ? GzipStreamSource::GZIP_STREAM_SOURCE_DEFLATE | |
| 82 : GzipStreamSource::GZIP_STREAM_SOURCE_GZIP; | |
| 83 bool fallback = type == StreamSource::SOURCE_GZIP_FALLBACK; | |
| 84 if (next->Init(mode, fallback)) | |
| 85 current = std::move(next); | |
|
mmenke
2016/03/04 21:15:56
On failure, just return NULL?
xunjieli
2016/04/20 19:16:09
Done.
| |
| 86 } | |
| 87 return current; | |
| 88 } | |
| 89 | |
| 90 } // namespace | |
| 91 | |
| 92 StreamSource::StreamSource(SourceType type, scoped_ptr<StreamSource> previous) | |
| 93 : type_(type), previous_(std::move(previous)) { | |
| 94 // Initializes |buffer_| if only the stream source needs to read input data | |
| 95 // from |previous_|. | |
| 96 if (previous_) | |
| 97 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.
| |
| 98 } | |
| 99 | |
| 100 StreamSource::~StreamSource() {} | |
| 101 | |
| 102 Error StreamSource::Read(IOBuffer* dest_buffer, | |
| 103 size_t buffer_size, | |
| 104 size_t* bytes_read, | |
| 105 const OnReadCompleteCallback& callback) { | |
| 106 *bytes_read = 0; | |
| 107 | |
| 108 Error error = OK; | |
| 109 while (error == OK) { | |
| 110 size_t single_bytes_read = 0; | |
| 111 error = ReadInternal(dest_buffer, buffer_size, &single_bytes_read); | |
| 112 *bytes_read += single_bytes_read; | |
| 113 | |
| 114 // ReadInternal() returns synchronously or an error occurred, return right | |
| 115 // here. | |
| 116 if ((error == OK && single_bytes_read > 0) || | |
| 117 (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.
| |
| 118 return error; | |
| 119 | |
| 120 // Needs more input, it has consumed all existing input. | |
| 121 DCHECK(!buffer_ || !buffer_->HasMoreBytes()); | |
| 122 | |
| 123 if (!previous_) | |
| 124 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.
| |
| 125 | |
| 126 // Dispatch a read to refill the input buffer. | |
| 127 size_t previous_bytes_read = 0; | |
| 128 error = previous_->Read( | |
| 129 buffer_->buffer(), buffer_->size(), &previous_bytes_read, | |
| 130 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.
| |
| 131 base::Unretained(dest_buffer), buffer_size)); | |
| 132 | |
| 133 // OK with 0 bytes read means EOF. Since the buffer is already empty, and | |
| 134 // 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.
| |
| 135 // at EOF. Just return that synchronously. | |
| 136 if (error == OK && previous_bytes_read == 0) | |
| 137 return OK; | |
| 138 | |
| 139 // If the underlying read completed synchronously, mark the buffer as | |
| 140 // refilled and try again. | |
| 141 if (error == OK) | |
| 142 buffer_->WasRefilled(previous_bytes_read); | |
| 143 } | |
| 144 | |
| 145 DCHECK(!buffer_ || !buffer_->HasMoreBytes()); | |
| 146 | |
| 147 if (error == ERR_IO_PENDING) { | |
| 148 callback_ = callback; | |
| 149 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.
| |
| 150 } | |
| 151 return error; | |
| 152 } | |
| 153 | |
| 154 void StreamSource::OnReadComplete(IOBuffer* dest_buffer, | |
| 155 size_t dest_buffer_size, | |
| 156 Error error, | |
| 157 size_t bytes_read) { | |
| 158 DCHECK(!buffer_ || !buffer_->HasMoreBytes()); | |
| 159 DCHECK_EQ(dest_buffer, pending_read_buffer_.get()); | |
| 160 DCHECK(!callback_.is_null()); | |
| 161 | |
| 162 // Take a ref for the lifetime of this function. | |
| 163 scoped_refptr<IOBuffer> dest_ref(dest_buffer); | |
| 164 pending_read_buffer_ = nullptr; | |
| 165 | |
| 166 // If the underlying read failed, fail this read directly. | |
| 167 if (error != OK) { | |
| 168 base::ResetAndReturn(&callback_).Run(error, bytes_read); | |
| 169 return; | |
| 170 } | |
| 171 | |
| 172 if (bytes_read == 0) { | |
| 173 // EOF. Since the buffer is empty, there is no more data to decompress (any | |
| 174 // internally buffered data would have been drained already before calling | |
| 175 // the previous stream's Read). Return EOF to our caller. | |
| 176 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.
| |
| 177 base::ResetAndReturn(&callback_).Run(error, bytes_read); | |
| 178 } | |
| 179 return; | |
| 180 } | |
| 181 | |
| 182 // Mark the buffer as refilled and try decompressing. | |
| 183 buffer_->WasRefilled(bytes_read); | |
| 184 | |
| 185 // 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.
| |
| 186 // Otherwise, Read will have posted an asynchronous read that | |
| 187 // will later re-invoke OnReadComplete to run the callback. | |
| 188 error = Read(dest_buffer, dest_buffer_size, &bytes_read, callback_); | |
| 189 if (error != ERR_IO_PENDING) | |
| 190 base::ResetAndReturn(&callback_).Run(error, bytes_read); | |
| 191 } | |
| 192 | |
| 193 scoped_ptr<StreamSource> StreamSource::BuildSourceChain( | |
| 194 scoped_ptr<StreamSource> current, | |
| 195 const std::vector<std::string>& type_names, | |
| 196 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.
| |
| 197 std::vector<SourceType> types = SourceTypeNamesToTypes(type_names); | |
| 198 | |
| 199 // SDCH-specific hack: if the first filter is SDCH, add a gzip filter in front | |
| 200 // of it in fallback mode. | |
| 201 if (!types.empty() && types.at(0) == SOURCE_SDCH) | |
| 202 types.insert(types.begin(), SOURCE_GZIP_FALLBACK); | |
| 203 | |
| 204 for (const auto& type : types) { | |
| 205 current = BuildSource(std::move(current), type, sdch_delegate); | |
| 206 if (current == nullptr) | |
| 207 return nullptr; | |
| 208 } | |
| 209 | |
| 210 return current; | |
| 211 } | |
| 212 | |
| 213 std::string StreamSource::OrderedStreamSourceList() const { | |
| 214 if (previous_) { | |
| 215 return SourceTypeAsString(type_) + "," + | |
| 216 previous_->OrderedStreamSourceList(); | |
| 217 } else { | |
| 218 return SourceTypeAsString(type_); | |
| 219 } | |
| 220 } | |
| 221 | |
| 222 } // namespace net | |
| OLD | NEW |