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

Side by Side Diff: net/filter/brotli_source_stream.cc

Issue 2329973003: Add net::BrotliSourceStream (Closed)
Patch Set: Sync-ed to 92104a0503b2862f54d60473b59dd1ae145eb22b 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 unified diff | Download patch
« no previous file with comments | « net/filter/brotli_source_stream.h ('k') | net/filter/brotli_source_stream_disabled.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2015 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "net/filter/brotli_filter.h" 5 #include "net/filter/brotli_source_stream.h"
6 6
7 #include "base/bind.h"
7 #include "base/bit_cast.h" 8 #include "base/bit_cast.h"
9 #include "base/logging.h"
8 #include "base/macros.h" 10 #include "base/macros.h"
11 #include "base/memory/ptr_util.h"
9 #include "base/metrics/histogram_macros.h" 12 #include "base/metrics/histogram_macros.h"
10 #include "base/numerics/safe_conversions.h" 13 #include "net/base/io_buffer.h"
11 #include "base/numerics/safe_math.h"
12 #include "third_party/brotli/dec/decode.h" 14 #include "third_party/brotli/dec/decode.h"
13 15
14 namespace net { 16 namespace net {
15 17
16 namespace { 18 namespace {
19
20 const char kBrotli[] = "BROTLI";
17 const uint8_t kGzipHeader[] = {0x1f, 0x8b, 0x08}; 21 const uint8_t kGzipHeader[] = {0x1f, 0x8b, 0x08};
18 }
19 22
20 // BrotliFilter applies Brotli content decoding to a data stream. 23 // BrotliSourceStream applies Brotli content decoding to a data stream.
21 // Brotli format specification: http://www.ietf.org/id/draft-alakuijala-brotli 24 // Brotli format specification: http://www.ietf.org/id/draft-alakuijala-brotli.
22 // 25 class BrotliSourceStream : public FilterSourceStream {
23 // BrotliFilter is a subclass of Filter. See the latter's header file filter.h
24 // for sample usage.
25 class BrotliFilter : public Filter {
26 public: 26 public:
27 BrotliFilter(FilterType type) 27 explicit BrotliSourceStream(std::unique_ptr<SourceStream> upstream)
28 : Filter(type), 28 : FilterSourceStream(SourceStream::TYPE_BROTLI, std::move(upstream)),
29 decoding_status_(DecodingStatus::DECODING_IN_PROGRESS), 29 decoding_status_(DecodingStatus::DECODING_IN_PROGRESS),
30 used_memory_(0), 30 used_memory_(0),
31 used_memory_maximum_(0), 31 used_memory_maximum_(0),
32 consumed_bytes_(0), 32 consumed_bytes_(0),
33 produced_bytes_(0), 33 produced_bytes_(0),
34 gzip_header_detected_(true) { 34 gzip_header_detected_(true) {
35 brotli_state_ = BrotliCreateState(BrotliFilter::AllocateMemory, 35 brotli_state_ = BrotliCreateState(AllocateMemory, FreeMemory, this);
36 BrotliFilter::FreeMemory, this);
37 CHECK(brotli_state_); 36 CHECK(brotli_state_);
38 } 37 }
39 38
40 ~BrotliFilter() override { 39 ~BrotliSourceStream() override {
41 BrotliErrorCode error_code = BrotliGetErrorCode(brotli_state_); 40 BrotliErrorCode error_code = BrotliGetErrorCode(brotli_state_);
42 BrotliDestroyState(brotli_state_); 41 BrotliDestroyState(brotli_state_);
43 brotli_state_ = nullptr; 42 brotli_state_ = nullptr;
44 DCHECK(used_memory_ == 0); 43 DCHECK_EQ(0u, used_memory_);
45 44
46 // Don't report that gzip header was detected in case of lack of input. 45 // Don't report that gzip header was detected in case of lack of input.
47 gzip_header_detected_ &= (consumed_bytes_ >= sizeof(kGzipHeader)); 46 gzip_header_detected_ &= (consumed_bytes_ >= sizeof(kGzipHeader));
48 47
49 UMA_HISTOGRAM_ENUMERATION( 48 UMA_HISTOGRAM_ENUMERATION(
50 "BrotliFilter.Status", static_cast<int>(decoding_status_), 49 "BrotliFilter.Status", static_cast<int>(decoding_status_),
51 static_cast<int>(DecodingStatus::DECODING_STATUS_COUNT)); 50 static_cast<int>(DecodingStatus::DECODING_STATUS_COUNT));
52 UMA_HISTOGRAM_BOOLEAN("BrotliFilter.GzipHeaderDetected", 51 UMA_HISTOGRAM_BOOLEAN("BrotliFilter.GzipHeaderDetected",
53 gzip_header_detected_); 52 gzip_header_detected_);
54 if (decoding_status_ == DecodingStatus::DECODING_DONE) { 53 if (decoding_status_ == DecodingStatus::DECODING_DONE) {
55 // CompressionPercent is undefined when there is no output produced. 54 // CompressionPercent is undefined when there is no output produced.
56 if (produced_bytes_ != 0) { 55 if (produced_bytes_ != 0) {
57 UMA_HISTOGRAM_PERCENTAGE( 56 UMA_HISTOGRAM_PERCENTAGE(
58 "BrotliFilter.CompressionPercent", 57 "BrotliFilter.CompressionPercent",
59 static_cast<int>((consumed_bytes_ * 100) / produced_bytes_)); 58 static_cast<int>((consumed_bytes_ * 100) / produced_bytes_));
60 } 59 }
61 } 60 }
62 if (error_code < 0) { 61 if (error_code < 0) {
63 UMA_HISTOGRAM_ENUMERATION("BrotliFilter.ErrorCode", 62 UMA_HISTOGRAM_ENUMERATION("BrotliFilter.ErrorCode",
64 -static_cast<int>(error_code), 63 -static_cast<int>(error_code),
65 1 - BROTLI_LAST_ERROR_CODE); 64 1 - BROTLI_LAST_ERROR_CODE);
66 } 65 }
67 66
68 // All code here is for gathering stats, and can be removed when 67 // All code here is for gathering stats, and can be removed when
69 // BrotliFilter is considered stable. 68 // BrotliSourceStream is considered stable.
70 static const int kBuckets = 48; 69 const int kBuckets = 48;
71 static const int64_t kMaxKb = 1 << (kBuckets / 3); // 64MiB in KiB 70 const int64_t kMaxKb = 1 << (kBuckets / 3); // 64MiB in KiB
72 UMA_HISTOGRAM_CUSTOM_COUNTS("BrotliFilter.UsedMemoryKB", 71 UMA_HISTOGRAM_CUSTOM_COUNTS("BrotliFilter.UsedMemoryKB",
73 used_memory_maximum_ / 1024, 1, kMaxKb, 72 used_memory_maximum_ / 1024, 1, kMaxKb,
74 kBuckets); 73 kBuckets);
75 } 74 }
76 75
77 // Decodes the pre-filter data and writes the output into the |dest_buffer| 76 private:
78 // passed in. 77 // Reported in UMA and must be kept in sync with the histograms.xml file.
79 // The function returns FilterStatus. See filter.h for its description. 78 enum class DecodingStatus : int {
80 // 79 DECODING_IN_PROGRESS = 0,
81 // Upon entry, |*dest_len| is the total size (in number of chars) of the 80 DECODING_DONE,
82 // destination buffer. Upon exit, |*dest_len| is the actual number of chars 81 DECODING_ERROR,
83 // written into the destination buffer.
84 //
85 // This function will fail if there is no pre-filter data in the
86 // |stream_buffer_|. On the other hand, |*dest_len| can be 0 upon successful
87 // return. For example, decompressor may process some pre-filter data
88 // but not produce output yet.
89 FilterStatus ReadFilteredData(char* dest_buffer, int* dest_len) override {
90 if (!dest_buffer || !dest_len)
91 return Filter::FILTER_ERROR;
92 82
93 if (decoding_status_ == DecodingStatus::DECODING_DONE) { 83 DECODING_STATUS_COUNT
94 *dest_len = 0; 84 // DECODING_STATUS_COUNT must always be the last element in this enum.
95 return Filter::FILTER_DONE; 85 };
96 }
97 86
98 if (decoding_status_ != DecodingStatus::DECODING_IN_PROGRESS) 87 // SourceStream implementation
99 return Filter::FILTER_ERROR; 88 std::string GetTypeAsString() const override { return kBrotli; }
100 89
101 size_t output_buffer_size = base::checked_cast<size_t>(*dest_len); 90 int FilterData(IOBuffer* output_buffer,
102 size_t input_buffer_size = base::checked_cast<size_t>(stream_data_len_); 91 int output_buffer_size,
103 92 IOBuffer* input_buffer,
93 int input_buffer_size,
94 int* consumed_bytes,
95 bool /*upstream_eof_reached*/) override {
96 const uint8_t* next_in = bit_cast<uint8_t*>(input_buffer->data());
104 size_t available_in = input_buffer_size; 97 size_t available_in = input_buffer_size;
105 const uint8_t* next_in = bit_cast<uint8_t*>(next_stream_data_); 98 uint8_t* next_out = bit_cast<uint8_t*>(output_buffer->data());
106 size_t available_out = output_buffer_size; 99 size_t available_out = output_buffer_size;
107 uint8_t* next_out = bit_cast<uint8_t*>(dest_buffer);
108 size_t total_out = 0; 100 size_t total_out = 0;
109
110 // Check if start of the input stream looks like gzip stream. 101 // Check if start of the input stream looks like gzip stream.
111 for (size_t i = consumed_bytes_; i < sizeof(kGzipHeader); ++i) { 102 for (size_t i = consumed_bytes_; i < sizeof(kGzipHeader); ++i) {
112 if (!gzip_header_detected_) 103 if (!gzip_header_detected_)
113 break; 104 break;
114 size_t j = i - consumed_bytes_; 105 size_t j = i - consumed_bytes_;
115 if (j < available_in && kGzipHeader[i] != next_in[j]) 106 if (j < available_in && kGzipHeader[i] != next_in[j])
116 gzip_header_detected_ = false; 107 gzip_header_detected_ = false;
117 } 108 }
118 109
119 BrotliResult result = 110 BrotliResult result =
120 BrotliDecompressStream(&available_in, &next_in, &available_out, 111 BrotliDecompressStream(&available_in, &next_in, &available_out,
121 &next_out, &total_out, brotli_state_); 112 &next_out, &total_out, brotli_state_);
122 113
123 CHECK(available_in <= input_buffer_size); 114 size_t bytes_used = input_buffer_size - available_in;
124 CHECK(available_out <= output_buffer_size); 115 size_t bytes_written = output_buffer_size - available_out;
125 consumed_bytes_ += input_buffer_size - available_in; 116 CHECK_GE(bytes_used, 0u);
126 produced_bytes_ += output_buffer_size - available_out; 117 CHECK_GE(bytes_written, 0u);
118 produced_bytes_ += bytes_written;
119 consumed_bytes_ += bytes_used;
127 120
128 base::CheckedNumeric<size_t> safe_bytes_written(output_buffer_size); 121 *consumed_bytes = bytes_used;
129 safe_bytes_written -= available_out;
130 int bytes_written =
131 base::checked_cast<int>(safe_bytes_written.ValueOrDie());
132 122
133 switch (result) { 123 switch (result) {
134 case BROTLI_RESULT_NEEDS_MORE_OUTPUT: 124 case BROTLI_RESULT_NEEDS_MORE_OUTPUT:
135 // Fall through. 125 return bytes_written;
136 case BROTLI_RESULT_SUCCESS: 126 case BROTLI_RESULT_SUCCESS:
137 *dest_len = bytes_written; 127 decoding_status_ = DecodingStatus::DECODING_DONE;
138 stream_data_len_ = base::checked_cast<int>(available_in); 128 return bytes_written;
139 next_stream_data_ = bit_cast<char*>(next_in);
140 if (result == BROTLI_RESULT_SUCCESS) {
141 decoding_status_ = DecodingStatus::DECODING_DONE;
142 return Filter::FILTER_DONE;
143 }
144 return Filter::FILTER_OK;
145
146 case BROTLI_RESULT_NEEDS_MORE_INPUT: 129 case BROTLI_RESULT_NEEDS_MORE_INPUT:
147 *dest_len = bytes_written; 130 // Decompress needs more input has consumed all existing input.
148 stream_data_len_ = 0; 131 DCHECK_EQ(*consumed_bytes, input_buffer_size);
149 next_stream_data_ = nullptr; 132 decoding_status_ = DecodingStatus::DECODING_IN_PROGRESS;
150 return Filter::FILTER_NEED_MORE_DATA; 133 return bytes_written;
151 134 // If the decompressor threw an error, fail synchronously.
152 default: 135 default:
153 decoding_status_ = DecodingStatus::DECODING_ERROR; 136 decoding_status_ = DecodingStatus::DECODING_ERROR;
154 return Filter::FILTER_ERROR; 137 return ERR_CONTENT_DECODING_FAILED;
155 } 138 }
156 } 139 }
157 140
158 private:
159 static void* AllocateMemory(void* opaque, size_t size) { 141 static void* AllocateMemory(void* opaque, size_t size) {
160 BrotliFilter* filter = reinterpret_cast<BrotliFilter*>(opaque); 142 BrotliSourceStream* filter = reinterpret_cast<BrotliSourceStream*>(opaque);
161 return filter->AllocateMemoryInternal(size); 143 return filter->AllocateMemoryInternal(size);
162 } 144 }
163 145
164 static void FreeMemory(void* opaque, void* address) { 146 static void FreeMemory(void* opaque, void* address) {
165 BrotliFilter* filter = reinterpret_cast<BrotliFilter*>(opaque); 147 BrotliSourceStream* filter = reinterpret_cast<BrotliSourceStream*>(opaque);
166 filter->FreeMemoryInternal(address); 148 filter->FreeMemoryInternal(address);
167 } 149 }
168 150
169 void* AllocateMemoryInternal(size_t size) { 151 void* AllocateMemoryInternal(size_t size) {
170 size_t* array = reinterpret_cast<size_t*>(malloc(size + sizeof(size_t))); 152 size_t* array = reinterpret_cast<size_t*>(malloc(size + sizeof(size_t)));
171 if (!array) 153 if (!array)
172 return nullptr; 154 return nullptr;
173 used_memory_ += size; 155 used_memory_ += size;
174 if (used_memory_maximum_ < used_memory_) 156 if (used_memory_maximum_ < used_memory_)
175 used_memory_maximum_ = used_memory_; 157 used_memory_maximum_ = used_memory_;
176 array[0] = size; 158 array[0] = size;
177 return &array[1]; 159 return &array[1];
178 } 160 }
179 161
180 void FreeMemoryInternal(void* address) { 162 void FreeMemoryInternal(void* address) {
181 if (!address) 163 if (!address)
182 return; 164 return;
183 size_t* array = reinterpret_cast<size_t*>(address); 165 size_t* array = reinterpret_cast<size_t*>(address);
184 used_memory_ -= array[-1]; 166 used_memory_ -= array[-1];
185 free(&array[-1]); 167 free(&array[-1]);
186 } 168 }
187 169
188 // Reported in UMA and must be kept in sync with the histograms.xml file. 170 BrotliState* brotli_state_;
189 enum class DecodingStatus : int {
190 DECODING_IN_PROGRESS = 0,
191 DECODING_DONE,
192 DECODING_ERROR,
193 171
194 DECODING_STATUS_COUNT
195 // DECODING_STATUS_COUNT must always be the last element in this enum.
196 };
197
198 // Tracks the status of decoding.
199 // This variable is updated only by ReadFilteredData.
200 DecodingStatus decoding_status_; 172 DecodingStatus decoding_status_;
201 173
202 BrotliState* brotli_state_;
203
204 size_t used_memory_; 174 size_t used_memory_;
205 size_t used_memory_maximum_; 175 size_t used_memory_maximum_;
206 size_t consumed_bytes_; 176 size_t consumed_bytes_;
207 size_t produced_bytes_; 177 size_t produced_bytes_;
208 178
209 bool gzip_header_detected_; 179 bool gzip_header_detected_;
210 180
211 DISALLOW_COPY_AND_ASSIGN(BrotliFilter); 181 DISALLOW_COPY_AND_ASSIGN(BrotliSourceStream);
212 }; 182 };
213 183
214 Filter* CreateBrotliFilter(Filter::FilterType type_id) { 184 } // namespace
215 return new BrotliFilter(type_id); 185
186 std::unique_ptr<FilterSourceStream> CreateBrotliSourceStream(
187 std::unique_ptr<SourceStream> previous) {
188 return base::WrapUnique(new BrotliSourceStream(std::move(previous)));
216 } 189 }
217 190
218 } // namespace net 191 } // namespace net
OLDNEW
« no previous file with comments | « net/filter/brotli_source_stream.h ('k') | net/filter/brotli_source_stream_disabled.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698