| 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 |