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