Probably broke Win7 Tests (dbg)(6). http://build.chromium.org/p/chromium.win/builders...
[chromium-blink-merge.git] / net / filter / gzip_filter.cc
blob36fe01cb1cecd81808b604819a8d80fe5049a4d4
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.
5 #include "net/filter/gzip_filter.h"
7 #include "base/logging.h"
8 #include "net/filter/gzip_header.h"
9 #include "third_party/zlib/zlib.h"
11 namespace net {
13 GZipFilter::GZipFilter()
14 : decoding_status_(DECODING_UNINITIALIZED),
15 decoding_mode_(DECODE_MODE_UNKNOWN),
16 gzip_header_status_(GZIP_CHECK_HEADER_IN_PROGRESS),
17 zlib_header_added_(false),
18 gzip_footer_bytes_(0),
19 possible_sdch_pass_through_(false) {
22 GZipFilter::~GZipFilter() {
23 if (decoding_status_ != DECODING_UNINITIALIZED) {
24 inflateEnd(zlib_stream_.get());
28 bool GZipFilter::InitDecoding(Filter::FilterType filter_type) {
29 if (decoding_status_ != DECODING_UNINITIALIZED)
30 return false;
32 // Initialize zlib control block
33 zlib_stream_.reset(new z_stream);
34 if (!zlib_stream_.get())
35 return false;
36 memset(zlib_stream_.get(), 0, sizeof(z_stream));
38 // Set decoding mode
39 switch (filter_type) {
40 case Filter::FILTER_TYPE_DEFLATE: {
41 if (inflateInit(zlib_stream_.get()) != Z_OK)
42 return false;
43 decoding_mode_ = DECODE_MODE_DEFLATE;
44 break;
46 case Filter::FILTER_TYPE_GZIP_HELPING_SDCH:
47 possible_sdch_pass_through_ = true; // Needed to optionally help sdch.
48 // Fall through to GZIP case.
49 case Filter::FILTER_TYPE_GZIP: {
50 gzip_header_.reset(new GZipHeader());
51 if (!gzip_header_.get())
52 return false;
53 if (inflateInit2(zlib_stream_.get(), -MAX_WBITS) != Z_OK)
54 return false;
55 decoding_mode_ = DECODE_MODE_GZIP;
56 break;
58 default: {
59 return false;
63 decoding_status_ = DECODING_IN_PROGRESS;
64 return true;
67 Filter::FilterStatus GZipFilter::ReadFilteredData(char* dest_buffer,
68 int* dest_len) {
69 if (!dest_buffer || !dest_len || *dest_len <= 0)
70 return Filter::FILTER_ERROR;
72 if (decoding_status_ == DECODING_DONE) {
73 if (GZIP_GET_INVALID_HEADER != gzip_header_status_)
74 SkipGZipFooter();
75 // Some server might send extra data after the gzip footer. We just copy
76 // them out. Mozilla does this too.
77 return CopyOut(dest_buffer, dest_len);
80 if (decoding_status_ != DECODING_IN_PROGRESS)
81 return Filter::FILTER_ERROR;
83 Filter::FilterStatus status;
85 if (decoding_mode_ == DECODE_MODE_GZIP &&
86 gzip_header_status_ == GZIP_CHECK_HEADER_IN_PROGRESS) {
87 // With gzip encoding the content is wrapped with a gzip header.
88 // We need to parse and verify the header first.
89 status = CheckGZipHeader();
90 switch (status) {
91 case Filter::FILTER_NEED_MORE_DATA: {
92 // We have consumed all input data, either getting a complete header or
93 // a partial header. Return now to get more data.
94 *dest_len = 0;
95 // Partial header means it can't be an SDCH header.
96 // Reason: SDCH *always* starts with 8 printable characters [a-zA-Z/_].
97 // Gzip always starts with two non-printable characters. Hence even a
98 // single character (partial header) means that this can't be an SDCH
99 // encoded body masquerading as a GZIP body.
100 possible_sdch_pass_through_ = false;
101 return status;
103 case Filter::FILTER_OK: {
104 // The header checking succeeds, and there are more data in the input.
105 // We must have got a complete header here.
106 DCHECK_EQ(gzip_header_status_, GZIP_GET_COMPLETE_HEADER);
107 break;
109 case Filter::FILTER_ERROR: {
110 if (possible_sdch_pass_through_ &&
111 GZIP_GET_INVALID_HEADER == gzip_header_status_) {
112 decoding_status_ = DECODING_DONE; // Become a pass through filter.
113 return CopyOut(dest_buffer, dest_len);
115 decoding_status_ = DECODING_ERROR;
116 return status;
118 default: {
119 status = Filter::FILTER_ERROR; // Unexpected.
120 decoding_status_ = DECODING_ERROR;
121 return status;
126 int dest_orig_size = *dest_len;
127 status = DoInflate(dest_buffer, dest_len);
129 if (decoding_mode_ == DECODE_MODE_DEFLATE && status == Filter::FILTER_ERROR) {
130 // As noted in Mozilla implementation, some servers such as Apache with
131 // mod_deflate don't generate zlib headers.
132 // See 677409 for instances where this work around is needed.
133 // Insert a dummy zlib header and try again.
134 if (InsertZlibHeader()) {
135 *dest_len = dest_orig_size;
136 status = DoInflate(dest_buffer, dest_len);
140 if (status == Filter::FILTER_DONE) {
141 decoding_status_ = DECODING_DONE;
142 } else if (status == Filter::FILTER_ERROR) {
143 decoding_status_ = DECODING_ERROR;
146 return status;
149 Filter::FilterStatus GZipFilter::CheckGZipHeader() {
150 DCHECK_EQ(gzip_header_status_, GZIP_CHECK_HEADER_IN_PROGRESS);
152 // Check input data in pre-filter buffer.
153 if (!next_stream_data_ || stream_data_len_ <= 0)
154 return Filter::FILTER_ERROR;
156 const char* header_end = NULL;
157 GZipHeader::Status header_status;
158 header_status = gzip_header_->ReadMore(next_stream_data_, stream_data_len_,
159 &header_end);
161 switch (header_status) {
162 case GZipHeader::INCOMPLETE_HEADER: {
163 // We read all the data but only got a partial header.
164 next_stream_data_ = NULL;
165 stream_data_len_ = 0;
166 return Filter::FILTER_NEED_MORE_DATA;
168 case GZipHeader::COMPLETE_HEADER: {
169 // We have a complete header. Check whether there are more data.
170 int num_chars_left = static_cast<int>(stream_data_len_ -
171 (header_end - next_stream_data_));
172 gzip_header_status_ = GZIP_GET_COMPLETE_HEADER;
174 if (num_chars_left > 0) {
175 next_stream_data_ = const_cast<char*>(header_end);
176 stream_data_len_ = num_chars_left;
177 return Filter::FILTER_OK;
178 } else {
179 next_stream_data_ = NULL;
180 stream_data_len_ = 0;
181 return Filter::FILTER_NEED_MORE_DATA;
184 case GZipHeader::INVALID_HEADER: {
185 gzip_header_status_ = GZIP_GET_INVALID_HEADER;
186 return Filter::FILTER_ERROR;
188 default: {
189 break;
193 return Filter::FILTER_ERROR;
196 Filter::FilterStatus GZipFilter::DoInflate(char* dest_buffer, int* dest_len) {
197 // Make sure we have both valid input data and output buffer.
198 if (!dest_buffer || !dest_len || *dest_len <= 0) // output
199 return Filter::FILTER_ERROR;
201 if (!next_stream_data_ || stream_data_len_ <= 0) { // input
202 *dest_len = 0;
203 return Filter::FILTER_NEED_MORE_DATA;
206 // Fill in zlib control block
207 zlib_stream_.get()->next_in = bit_cast<Bytef*>(next_stream_data_);
208 zlib_stream_.get()->avail_in = stream_data_len_;
209 zlib_stream_.get()->next_out = bit_cast<Bytef*>(dest_buffer);
210 zlib_stream_.get()->avail_out = *dest_len;
212 int inflate_code = inflate(zlib_stream_.get(), Z_NO_FLUSH);
213 int bytesWritten = *dest_len - zlib_stream_.get()->avail_out;
215 Filter::FilterStatus status;
217 switch (inflate_code) {
218 case Z_STREAM_END: {
219 *dest_len = bytesWritten;
221 stream_data_len_ = zlib_stream_.get()->avail_in;
222 next_stream_data_ = bit_cast<char*>(zlib_stream_.get()->next_in);
224 SkipGZipFooter();
226 status = Filter::FILTER_DONE;
227 break;
229 case Z_BUF_ERROR: {
230 // According to zlib documentation, when calling inflate with Z_NO_FLUSH,
231 // getting Z_BUF_ERROR means no progress is possible. Neither processing
232 // more input nor producing more output can be done.
233 // Since we have checked both input data and output buffer before calling
234 // inflate, this result is unexpected.
235 status = Filter::FILTER_ERROR;
236 break;
238 case Z_OK: {
239 // Some progress has been made (more input processed or more output
240 // produced).
241 *dest_len = bytesWritten;
243 // Check whether we have consumed all input data.
244 stream_data_len_ = zlib_stream_.get()->avail_in;
245 if (stream_data_len_ == 0) {
246 next_stream_data_ = NULL;
247 status = Filter::FILTER_NEED_MORE_DATA;
248 } else {
249 next_stream_data_ = bit_cast<char*>(zlib_stream_.get()->next_in);
250 status = Filter::FILTER_OK;
252 break;
254 default: {
255 status = Filter::FILTER_ERROR;
256 break;
260 return status;
263 bool GZipFilter::InsertZlibHeader() {
264 static char dummy_head[2] = { 0x78, 0x1 };
266 char dummy_output[4];
268 // We only try add additional header once.
269 if (zlib_header_added_)
270 return false;
272 inflateReset(zlib_stream_.get());
273 zlib_stream_.get()->next_in = bit_cast<Bytef*>(&dummy_head[0]);
274 zlib_stream_.get()->avail_in = sizeof(dummy_head);
275 zlib_stream_.get()->next_out = bit_cast<Bytef*>(&dummy_output[0]);
276 zlib_stream_.get()->avail_out = sizeof(dummy_output);
278 int code = inflate(zlib_stream_.get(), Z_NO_FLUSH);
279 zlib_header_added_ = true;
281 return (code == Z_OK);
285 void GZipFilter::SkipGZipFooter() {
286 int footer_bytes_expected = kGZipFooterSize - gzip_footer_bytes_;
287 if (footer_bytes_expected > 0) {
288 int footer_byte_avail = std::min(footer_bytes_expected, stream_data_len_);
289 stream_data_len_ -= footer_byte_avail;
290 next_stream_data_ += footer_byte_avail;
291 gzip_footer_bytes_ += footer_byte_avail;
293 if (stream_data_len_ == 0)
294 next_stream_data_ = NULL;
298 } // namespace net