| OLD | NEW | 
|---|
|  | (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 |  | 
| OLD | NEW | 
|---|