| OLD | NEW |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 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_filter.h" |
| 6 | 6 |
| 7 #include "base/bit_cast.h" | 7 #include "base/bit_cast.h" |
| 8 #include "base/macros.h" | 8 #include "base/macros.h" |
| 9 #include "base/metrics/histogram_macros.h" | 9 #include "base/metrics/histogram_macros.h" |
| 10 #include "base/numerics/safe_conversions.h" | 10 #include "base/numerics/safe_conversions.h" |
| 11 #include "base/numerics/safe_math.h" | 11 #include "base/numerics/safe_math.h" |
| 12 #include "third_party/brotli/dec/decode.h" | 12 #include "third_party/brotli/dec/decode.h" |
| 13 | 13 |
| 14 namespace net { | 14 namespace net { |
| 15 | 15 |
| 16 namespace { |
| 17 const uint8_t kGzipHeader[] = {0x1f, 0x8b, 0x08}; |
| 18 } |
| 19 |
| 16 // BrotliFilter applies Brotli content decoding to a data stream. | 20 // BrotliFilter applies Brotli content decoding to a data stream. |
| 17 // Brotli format specification: http://www.ietf.org/id/draft-alakuijala-brotli | 21 // Brotli format specification: http://www.ietf.org/id/draft-alakuijala-brotli |
| 18 // | 22 // |
| 19 // BrotliFilter is a subclass of Filter. See the latter's header file filter.h | 23 // BrotliFilter is a subclass of Filter. See the latter's header file filter.h |
| 20 // for sample usage. | 24 // for sample usage. |
| 21 class BrotliFilter : public Filter { | 25 class BrotliFilter : public Filter { |
| 22 public: | 26 public: |
| 23 BrotliFilter(FilterType type) | 27 BrotliFilter(FilterType type) |
| 24 : Filter(type), | 28 : Filter(type), |
| 25 decoding_status_(DecodingStatus::DECODING_IN_PROGRESS), | 29 decoding_status_(DecodingStatus::DECODING_IN_PROGRESS), |
| 26 used_memory_(0), | 30 used_memory_(0), |
| 27 used_memory_maximum_(0), | 31 used_memory_maximum_(0), |
| 28 consumed_bytes_(0), | 32 consumed_bytes_(0), |
| 29 produced_bytes_(0) { | 33 produced_bytes_(0), |
| 34 gzip_header_detected_(true) { |
| 30 brotli_state_ = BrotliCreateState(BrotliFilter::AllocateMemory, | 35 brotli_state_ = BrotliCreateState(BrotliFilter::AllocateMemory, |
| 31 BrotliFilter::FreeMemory, this); | 36 BrotliFilter::FreeMemory, this); |
| 32 CHECK(brotli_state_); | 37 CHECK(brotli_state_); |
| 33 } | 38 } |
| 34 | 39 |
| 35 ~BrotliFilter() override { | 40 ~BrotliFilter() override { |
| 41 BrotliErrorCode error_code = BrotliGetErrorCode(brotli_state_); |
| 36 BrotliDestroyState(brotli_state_); | 42 BrotliDestroyState(brotli_state_); |
| 37 brotli_state_ = nullptr; | 43 brotli_state_ = nullptr; |
| 38 DCHECK(used_memory_ == 0); | 44 DCHECK(used_memory_ == 0); |
| 39 | 45 |
| 46 // Don't report that gzip header was detected in case of lack of input. |
| 47 gzip_header_detected_ &= (consumed_bytes_ >= sizeof(kGzipHeader)); |
| 48 |
| 40 UMA_HISTOGRAM_ENUMERATION( | 49 UMA_HISTOGRAM_ENUMERATION( |
| 41 "BrotliFilter.Status", static_cast<int>(decoding_status_), | 50 "BrotliFilter.Status", static_cast<int>(decoding_status_), |
| 42 static_cast<int>(DecodingStatus::DECODING_STATUS_COUNT)); | 51 static_cast<int>(DecodingStatus::DECODING_STATUS_COUNT)); |
| 52 UMA_HISTOGRAM_BOOLEAN("BrotliFilter.GzipHeaderDetected", |
| 53 gzip_header_detected_); |
| 43 if (decoding_status_ == DecodingStatus::DECODING_DONE) { | 54 if (decoding_status_ == DecodingStatus::DECODING_DONE) { |
| 44 // CompressionPercent is undefined when there is no output produced. | 55 // CompressionPercent is undefined when there is no output produced. |
| 45 if (produced_bytes_ != 0) { | 56 if (produced_bytes_ != 0) { |
| 46 UMA_HISTOGRAM_PERCENTAGE( | 57 UMA_HISTOGRAM_PERCENTAGE( |
| 47 "BrotliFilter.CompressionPercent", | 58 "BrotliFilter.CompressionPercent", |
| 48 static_cast<int>((consumed_bytes_ * 100) / produced_bytes_)); | 59 static_cast<int>((consumed_bytes_ * 100) / produced_bytes_)); |
| 49 } | 60 } |
| 50 } | 61 } |
| 62 if (error_code < 0) { |
| 63 UMA_HISTOGRAM_ENUMERATION("BrotliFilter.ErrorCode", |
| 64 -static_cast<int>(error_code), |
| 65 1 - BROTLI_LAST_ERROR_CODE); |
| 66 } |
| 51 | 67 |
| 52 // All code here is for gathering stats, and can be removed when | 68 // All code here is for gathering stats, and can be removed when |
| 53 // BrotliFilter is considered stable. | 69 // BrotliFilter is considered stable. |
| 54 static const int kBuckets = 48; | 70 static const int kBuckets = 48; |
| 55 static const int64_t kMaxKb = 1 << (kBuckets / 3); // 64MiB in KiB | 71 static const int64_t kMaxKb = 1 << (kBuckets / 3); // 64MiB in KiB |
| 56 UMA_HISTOGRAM_CUSTOM_COUNTS("BrotliFilter.UsedMemoryKB", | 72 UMA_HISTOGRAM_CUSTOM_COUNTS("BrotliFilter.UsedMemoryKB", |
| 57 used_memory_maximum_ / 1024, 1, kMaxKb, | 73 used_memory_maximum_ / 1024, 1, kMaxKb, |
| 58 kBuckets); | 74 kBuckets); |
| 59 } | 75 } |
| 60 | 76 |
| (...skipping 22 matching lines...) Expand all Loading... |
| 83 return Filter::FILTER_ERROR; | 99 return Filter::FILTER_ERROR; |
| 84 | 100 |
| 85 size_t output_buffer_size = base::checked_cast<size_t>(*dest_len); | 101 size_t output_buffer_size = base::checked_cast<size_t>(*dest_len); |
| 86 size_t input_buffer_size = base::checked_cast<size_t>(stream_data_len_); | 102 size_t input_buffer_size = base::checked_cast<size_t>(stream_data_len_); |
| 87 | 103 |
| 88 size_t available_in = input_buffer_size; | 104 size_t available_in = input_buffer_size; |
| 89 const uint8_t* next_in = bit_cast<uint8_t*>(next_stream_data_); | 105 const uint8_t* next_in = bit_cast<uint8_t*>(next_stream_data_); |
| 90 size_t available_out = output_buffer_size; | 106 size_t available_out = output_buffer_size; |
| 91 uint8_t* next_out = bit_cast<uint8_t*>(dest_buffer); | 107 uint8_t* next_out = bit_cast<uint8_t*>(dest_buffer); |
| 92 size_t total_out = 0; | 108 size_t total_out = 0; |
| 109 |
| 110 // Check if start of the input stream looks like gzip stream. |
| 111 for (size_t i = consumed_bytes_; i < sizeof(kGzipHeader); ++i) { |
| 112 if (!gzip_header_detected_) |
| 113 break; |
| 114 size_t j = i - consumed_bytes_; |
| 115 if (j < available_in && kGzipHeader[i] != next_in[j]) |
| 116 gzip_header_detected_ = false; |
| 117 } |
| 118 |
| 93 BrotliResult result = | 119 BrotliResult result = |
| 94 BrotliDecompressStream(&available_in, &next_in, &available_out, | 120 BrotliDecompressStream(&available_in, &next_in, &available_out, |
| 95 &next_out, &total_out, brotli_state_); | 121 &next_out, &total_out, brotli_state_); |
| 96 | 122 |
| 97 CHECK(available_in <= input_buffer_size); | 123 CHECK(available_in <= input_buffer_size); |
| 98 CHECK(available_out <= output_buffer_size); | 124 CHECK(available_out <= output_buffer_size); |
| 99 consumed_bytes_ += input_buffer_size - available_in; | 125 consumed_bytes_ += input_buffer_size - available_in; |
| 100 produced_bytes_ += output_buffer_size - available_out; | 126 produced_bytes_ += output_buffer_size - available_out; |
| 101 | 127 |
| 102 base::CheckedNumeric<size_t> safe_bytes_written(output_buffer_size); | 128 base::CheckedNumeric<size_t> safe_bytes_written(output_buffer_size); |
| (...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 173 // This variable is updated only by ReadFilteredData. | 199 // This variable is updated only by ReadFilteredData. |
| 174 DecodingStatus decoding_status_; | 200 DecodingStatus decoding_status_; |
| 175 | 201 |
| 176 BrotliState* brotli_state_; | 202 BrotliState* brotli_state_; |
| 177 | 203 |
| 178 size_t used_memory_; | 204 size_t used_memory_; |
| 179 size_t used_memory_maximum_; | 205 size_t used_memory_maximum_; |
| 180 size_t consumed_bytes_; | 206 size_t consumed_bytes_; |
| 181 size_t produced_bytes_; | 207 size_t produced_bytes_; |
| 182 | 208 |
| 209 bool gzip_header_detected_; |
| 210 |
| 183 DISALLOW_COPY_AND_ASSIGN(BrotliFilter); | 211 DISALLOW_COPY_AND_ASSIGN(BrotliFilter); |
| 184 }; | 212 }; |
| 185 | 213 |
| 186 Filter* CreateBrotliFilter(Filter::FilterType type_id) { | 214 Filter* CreateBrotliFilter(Filter::FilterType type_id) { |
| 187 return new BrotliFilter(type_id); | 215 return new BrotliFilter(type_id); |
| 188 } | 216 } |
| 189 | 217 |
| 190 } // namespace net | 218 } // namespace net |
| OLD | NEW |