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

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

Issue 2478703004: Remove net::Filter code (Closed)
Patch Set: Address Matt's comment Created 4 years, 1 month 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/gzip_filter.h ('k') | net/filter/gzip_filter_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright 2014 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/gzip_filter.h"
6
7 #include "base/bit_cast.h"
8 #include "base/logging.h"
9 #include "net/filter/gzip_header.h"
10 #include "third_party/zlib/zlib.h"
11
12 namespace net {
13
14 GZipFilter::GZipFilter(FilterType type)
15 : Filter(type),
16 decoding_status_(DECODING_UNINITIALIZED),
17 decoding_mode_(DECODE_MODE_UNKNOWN),
18 gzip_header_status_(GZIP_CHECK_HEADER_IN_PROGRESS),
19 zlib_header_added_(false),
20 gzip_footer_bytes_(0),
21 possible_sdch_pass_through_(false) {
22 }
23
24 GZipFilter::~GZipFilter() {
25 if (decoding_status_ != DECODING_UNINITIALIZED) {
26 inflateEnd(zlib_stream_.get());
27 }
28 }
29
30 bool GZipFilter::InitDecoding(Filter::FilterType filter_type) {
31 if (decoding_status_ != DECODING_UNINITIALIZED)
32 return false;
33
34 // Initialize zlib control block
35 zlib_stream_.reset(new z_stream);
36 if (!zlib_stream_.get())
37 return false;
38 memset(zlib_stream_.get(), 0, sizeof(z_stream));
39
40 // Set decoding mode
41 switch (filter_type) {
42 case Filter::FILTER_TYPE_DEFLATE: {
43 if (inflateInit(zlib_stream_.get()) != Z_OK)
44 return false;
45 decoding_mode_ = DECODE_MODE_DEFLATE;
46 break;
47 }
48 case Filter::FILTER_TYPE_GZIP_HELPING_SDCH:
49 possible_sdch_pass_through_ = true; // Needed to optionally help sdch.
50 // Fall through to GZIP case.
51 case Filter::FILTER_TYPE_GZIP: {
52 gzip_header_.reset(new GZipHeader());
53 if (!gzip_header_.get())
54 return false;
55 if (inflateInit2(zlib_stream_.get(), -MAX_WBITS) != Z_OK)
56 return false;
57 decoding_mode_ = DECODE_MODE_GZIP;
58 break;
59 }
60 default: {
61 return false;
62 }
63 }
64
65 decoding_status_ = DECODING_IN_PROGRESS;
66 return true;
67 }
68
69 Filter::FilterStatus GZipFilter::ReadFilteredData(char* dest_buffer,
70 int* dest_len) {
71 if (!dest_buffer || !dest_len || *dest_len <= 0)
72 return Filter::FILTER_ERROR;
73
74 if (decoding_status_ == DECODING_DONE) {
75 if (GZIP_GET_INVALID_HEADER != gzip_header_status_)
76 SkipGZipFooter();
77 // Some server might send extra data after the gzip footer. We just copy
78 // them out. Mozilla does this too.
79 return CopyOut(dest_buffer, dest_len);
80 }
81
82 if (decoding_status_ != DECODING_IN_PROGRESS)
83 return Filter::FILTER_ERROR;
84
85 Filter::FilterStatus status;
86
87 if (decoding_mode_ == DECODE_MODE_GZIP &&
88 gzip_header_status_ == GZIP_CHECK_HEADER_IN_PROGRESS) {
89 // With gzip encoding the content is wrapped with a gzip header.
90 // We need to parse and verify the header first.
91 status = CheckGZipHeader();
92 switch (status) {
93 case Filter::FILTER_NEED_MORE_DATA: {
94 // We have consumed all input data, either getting a complete header or
95 // a partial header. Return now to get more data.
96 *dest_len = 0;
97 // Partial header means it can't be an SDCH header.
98 // Reason: SDCH *always* starts with 8 printable characters [a-zA-Z/_].
99 // Gzip always starts with two non-printable characters. Hence even a
100 // single character (partial header) means that this can't be an SDCH
101 // encoded body masquerading as a GZIP body.
102 possible_sdch_pass_through_ = false;
103 return status;
104 }
105 case Filter::FILTER_OK: {
106 // The header checking succeeds, and there are more data in the input.
107 // We must have got a complete header here.
108 DCHECK_EQ(gzip_header_status_, GZIP_GET_COMPLETE_HEADER);
109 break;
110 }
111 case Filter::FILTER_ERROR: {
112 if (possible_sdch_pass_through_ &&
113 GZIP_GET_INVALID_HEADER == gzip_header_status_) {
114 decoding_status_ = DECODING_DONE; // Become a pass through filter.
115 return CopyOut(dest_buffer, dest_len);
116 }
117 decoding_status_ = DECODING_ERROR;
118 return status;
119 }
120 default: {
121 status = Filter::FILTER_ERROR; // Unexpected.
122 decoding_status_ = DECODING_ERROR;
123 return status;
124 }
125 }
126 }
127
128 int dest_orig_size = *dest_len;
129 status = DoInflate(dest_buffer, dest_len);
130
131 if (decoding_mode_ == DECODE_MODE_DEFLATE && status == Filter::FILTER_ERROR) {
132 // As noted in Mozilla implementation, some servers such as Apache with
133 // mod_deflate don't generate zlib headers.
134 // See 677409 for instances where this work around is needed.
135 // Insert a dummy zlib header and try again.
136 if (InsertZlibHeader()) {
137 *dest_len = dest_orig_size;
138 status = DoInflate(dest_buffer, dest_len);
139 }
140 }
141
142 if (status == Filter::FILTER_DONE) {
143 decoding_status_ = DECODING_DONE;
144 } else if (status == Filter::FILTER_ERROR) {
145 decoding_status_ = DECODING_ERROR;
146 }
147
148 return status;
149 }
150
151 Filter::FilterStatus GZipFilter::CheckGZipHeader() {
152 DCHECK_EQ(gzip_header_status_, GZIP_CHECK_HEADER_IN_PROGRESS);
153
154 // Check input data in pre-filter buffer.
155 if (!next_stream_data_ || stream_data_len_ <= 0)
156 return Filter::FILTER_ERROR;
157
158 const char* header_end = NULL;
159 GZipHeader::Status header_status;
160 header_status = gzip_header_->ReadMore(next_stream_data_, stream_data_len_,
161 &header_end);
162
163 switch (header_status) {
164 case GZipHeader::INCOMPLETE_HEADER: {
165 // We read all the data but only got a partial header.
166 next_stream_data_ = NULL;
167 stream_data_len_ = 0;
168 return Filter::FILTER_NEED_MORE_DATA;
169 }
170 case GZipHeader::COMPLETE_HEADER: {
171 // We have a complete header. Check whether there are more data.
172 int num_chars_left = static_cast<int>(stream_data_len_ -
173 (header_end - next_stream_data_));
174 gzip_header_status_ = GZIP_GET_COMPLETE_HEADER;
175
176 if (num_chars_left > 0) {
177 next_stream_data_ = const_cast<char*>(header_end);
178 stream_data_len_ = num_chars_left;
179 return Filter::FILTER_OK;
180 } else {
181 next_stream_data_ = NULL;
182 stream_data_len_ = 0;
183 return Filter::FILTER_NEED_MORE_DATA;
184 }
185 }
186 case GZipHeader::INVALID_HEADER: {
187 gzip_header_status_ = GZIP_GET_INVALID_HEADER;
188 return Filter::FILTER_ERROR;
189 }
190 default: {
191 break;
192 }
193 }
194
195 return Filter::FILTER_ERROR;
196 }
197
198 Filter::FilterStatus GZipFilter::DoInflate(char* dest_buffer, int* dest_len) {
199 // Make sure we have both valid input data and output buffer.
200 if (!dest_buffer || !dest_len || *dest_len <= 0) // output
201 return Filter::FILTER_ERROR;
202
203 if (!next_stream_data_ || stream_data_len_ <= 0) { // input
204 *dest_len = 0;
205 return Filter::FILTER_NEED_MORE_DATA;
206 }
207
208 // Fill in zlib control block
209 zlib_stream_.get()->next_in = bit_cast<Bytef*>(next_stream_data_);
210 zlib_stream_.get()->avail_in = stream_data_len_;
211 zlib_stream_.get()->next_out = bit_cast<Bytef*>(dest_buffer);
212 zlib_stream_.get()->avail_out = *dest_len;
213
214 int inflate_code = inflate(zlib_stream_.get(), Z_NO_FLUSH);
215 int bytesWritten = *dest_len - zlib_stream_.get()->avail_out;
216
217 Filter::FilterStatus status;
218
219 switch (inflate_code) {
220 case Z_STREAM_END: {
221 *dest_len = bytesWritten;
222
223 stream_data_len_ = zlib_stream_.get()->avail_in;
224 next_stream_data_ = bit_cast<char*>(zlib_stream_.get()->next_in);
225
226 SkipGZipFooter();
227
228 status = Filter::FILTER_DONE;
229 break;
230 }
231 case Z_BUF_ERROR: {
232 // According to zlib documentation, when calling inflate with Z_NO_FLUSH,
233 // getting Z_BUF_ERROR means no progress is possible. Neither processing
234 // more input nor producing more output can be done.
235 // Since we have checked both input data and output buffer before calling
236 // inflate, this result is unexpected.
237 status = Filter::FILTER_ERROR;
238 break;
239 }
240 case Z_OK: {
241 // Some progress has been made (more input processed or more output
242 // produced).
243 *dest_len = bytesWritten;
244
245 // Check whether we have consumed all input data.
246 stream_data_len_ = zlib_stream_.get()->avail_in;
247 if (stream_data_len_ == 0) {
248 next_stream_data_ = NULL;
249 status = Filter::FILTER_NEED_MORE_DATA;
250 } else {
251 next_stream_data_ = bit_cast<char*>(zlib_stream_.get()->next_in);
252 status = Filter::FILTER_OK;
253 }
254 break;
255 }
256 default: {
257 status = Filter::FILTER_ERROR;
258 break;
259 }
260 }
261
262 return status;
263 }
264
265 bool GZipFilter::InsertZlibHeader() {
266 static char dummy_head[2] = { 0x78, 0x1 };
267
268 char dummy_output[4];
269
270 // We only try add additional header once.
271 if (zlib_header_added_)
272 return false;
273
274 inflateReset(zlib_stream_.get());
275 zlib_stream_.get()->next_in = bit_cast<Bytef*>(&dummy_head[0]);
276 zlib_stream_.get()->avail_in = sizeof(dummy_head);
277 zlib_stream_.get()->next_out = bit_cast<Bytef*>(&dummy_output[0]);
278 zlib_stream_.get()->avail_out = sizeof(dummy_output);
279
280 int code = inflate(zlib_stream_.get(), Z_NO_FLUSH);
281 zlib_header_added_ = true;
282
283 return (code == Z_OK);
284 }
285
286
287 void GZipFilter::SkipGZipFooter() {
288 int footer_bytes_expected = kGZipFooterSize - gzip_footer_bytes_;
289 if (footer_bytes_expected > 0) {
290 int footer_byte_avail = std::min(footer_bytes_expected, stream_data_len_);
291 stream_data_len_ -= footer_byte_avail;
292 next_stream_data_ += footer_byte_avail;
293 gzip_footer_bytes_ += footer_byte_avail;
294
295 if (stream_data_len_ == 0)
296 next_stream_data_ = NULL;
297 }
298 }
299
300 } // namespace net
OLDNEW
« no previous file with comments | « net/filter/gzip_filter.h ('k') | net/filter/gzip_filter_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698