| 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 <fstream> |  | 
|    8 #include <memory> |  | 
|    9 #include <ostream> |  | 
|   10  |  | 
|   11 #include "base/bit_cast.h" |  | 
|   12 #include "base/files/file_util.h" |  | 
|   13 #include "base/path_service.h" |  | 
|   14 #include "net/base/io_buffer.h" |  | 
|   15 #include "net/filter/mock_filter_context.h" |  | 
|   16 #include "testing/gtest/include/gtest/gtest.h" |  | 
|   17 #include "testing/platform_test.h" |  | 
|   18 #include "third_party/zlib/zlib.h" |  | 
|   19  |  | 
|   20 namespace { |  | 
|   21  |  | 
|   22 const int kDefaultBufferSize = 4096; |  | 
|   23 const int kSmallBufferSize = 128; |  | 
|   24  |  | 
|   25 // The GZIP header (see RFC 1952): |  | 
|   26 //   +---+---+---+---+---+---+---+---+---+---+ |  | 
|   27 //   |ID1|ID2|CM |FLG|     MTIME     |XFL|OS | |  | 
|   28 //   +---+---+---+---+---+---+---+---+---+---+ |  | 
|   29 //     ID1     \037 |  | 
|   30 //     ID2     \213 |  | 
|   31 //     CM      \010 (compression method == DEFLATE) |  | 
|   32 //     FLG     \000 (special flags that we do not support) |  | 
|   33 //     MTIME   Unix format modification time (0 means not available) |  | 
|   34 //     XFL     2-4? DEFLATE flags |  | 
|   35 //     OS      ???? Operating system indicator (255 means unknown) |  | 
|   36 // |  | 
|   37 // Header value we generate: |  | 
|   38 const char kGZipHeader[] = { '\037', '\213', '\010', '\000', '\000', |  | 
|   39                              '\000', '\000', '\000', '\002', '\377' }; |  | 
|   40  |  | 
|   41 enum EncodeMode { |  | 
|   42   ENCODE_GZIP,      // Wrap the deflate with a GZip header. |  | 
|   43   ENCODE_DEFLATE    // Raw deflate. |  | 
|   44 }; |  | 
|   45  |  | 
|   46 }  // namespace |  | 
|   47  |  | 
|   48 namespace net { |  | 
|   49  |  | 
|   50 // These tests use the path service, which uses autoreleased objects on the |  | 
|   51 // Mac, so this needs to be a PlatformTest. |  | 
|   52 class GZipUnitTest : public PlatformTest { |  | 
|   53  protected: |  | 
|   54   void SetUp() override { |  | 
|   55     PlatformTest::SetUp(); |  | 
|   56  |  | 
|   57     deflate_encode_buffer_ = NULL; |  | 
|   58     gzip_encode_buffer_ = NULL; |  | 
|   59  |  | 
|   60     // Get the path of source data file. |  | 
|   61     base::FilePath file_path; |  | 
|   62     PathService::Get(base::DIR_SOURCE_ROOT, &file_path); |  | 
|   63     file_path = file_path.AppendASCII("net"); |  | 
|   64     file_path = file_path.AppendASCII("data"); |  | 
|   65     file_path = file_path.AppendASCII("filter_unittests"); |  | 
|   66     file_path = file_path.AppendASCII("google.txt"); |  | 
|   67  |  | 
|   68     // Read data from the file into buffer. |  | 
|   69     ASSERT_TRUE(base::ReadFileToString(file_path, &source_buffer_)); |  | 
|   70  |  | 
|   71     // Encode the data with deflate |  | 
|   72     deflate_encode_buffer_ = new char[kDefaultBufferSize]; |  | 
|   73     ASSERT_TRUE(deflate_encode_buffer_ != NULL); |  | 
|   74  |  | 
|   75     deflate_encode_len_ = kDefaultBufferSize; |  | 
|   76     int code = CompressAll(ENCODE_DEFLATE , source_buffer(), source_len(), |  | 
|   77                            deflate_encode_buffer_, &deflate_encode_len_); |  | 
|   78     ASSERT_TRUE(code == Z_STREAM_END); |  | 
|   79     ASSERT_GT(deflate_encode_len_, 0); |  | 
|   80     ASSERT_TRUE(deflate_encode_len_ <= kDefaultBufferSize); |  | 
|   81  |  | 
|   82     // Encode the data with gzip |  | 
|   83     gzip_encode_buffer_ = new char[kDefaultBufferSize]; |  | 
|   84     ASSERT_TRUE(gzip_encode_buffer_ != NULL); |  | 
|   85  |  | 
|   86     gzip_encode_len_ = kDefaultBufferSize; |  | 
|   87     code = CompressAll(ENCODE_GZIP, source_buffer(), source_len(), |  | 
|   88                            gzip_encode_buffer_, &gzip_encode_len_); |  | 
|   89     ASSERT_TRUE(code == Z_STREAM_END); |  | 
|   90     ASSERT_GT(gzip_encode_len_, 0); |  | 
|   91     ASSERT_TRUE(gzip_encode_len_ <= kDefaultBufferSize); |  | 
|   92   } |  | 
|   93  |  | 
|   94   void TearDown() override { |  | 
|   95     delete[] deflate_encode_buffer_; |  | 
|   96     deflate_encode_buffer_ = NULL; |  | 
|   97  |  | 
|   98     delete[] gzip_encode_buffer_; |  | 
|   99     gzip_encode_buffer_ = NULL; |  | 
|  100  |  | 
|  101     PlatformTest::TearDown(); |  | 
|  102   } |  | 
|  103  |  | 
|  104   // Compress the data in source with deflate encoding and write output to the |  | 
|  105   // buffer provided by dest. The function returns Z_OK if success, and returns |  | 
|  106   // other zlib error code if fail. |  | 
|  107   // The parameter mode specifies the encoding mechanism. |  | 
|  108   // The dest buffer should be large enough to hold all the output data. |  | 
|  109   int CompressAll(EncodeMode mode, const char* source, int source_size, |  | 
|  110                   char* dest, int* dest_len) { |  | 
|  111     z_stream zlib_stream; |  | 
|  112     memset(&zlib_stream, 0, sizeof(zlib_stream)); |  | 
|  113     int code; |  | 
|  114  |  | 
|  115     // Initialize zlib |  | 
|  116     if (mode == ENCODE_GZIP) { |  | 
|  117       code = deflateInit2(&zlib_stream, Z_DEFAULT_COMPRESSION, Z_DEFLATED, |  | 
|  118                           -MAX_WBITS, |  | 
|  119                           8,  // DEF_MEM_LEVEL |  | 
|  120                           Z_DEFAULT_STRATEGY); |  | 
|  121     } else { |  | 
|  122       code = deflateInit(&zlib_stream, Z_DEFAULT_COMPRESSION); |  | 
|  123     } |  | 
|  124  |  | 
|  125     if (code != Z_OK) |  | 
|  126       return code; |  | 
|  127  |  | 
|  128     // Fill in zlib control block |  | 
|  129     zlib_stream.next_in = bit_cast<Bytef*>(source); |  | 
|  130     zlib_stream.avail_in = source_size; |  | 
|  131     zlib_stream.next_out = bit_cast<Bytef*>(dest); |  | 
|  132     zlib_stream.avail_out = *dest_len; |  | 
|  133  |  | 
|  134     // Write header if needed |  | 
|  135     if (mode == ENCODE_GZIP) { |  | 
|  136       if (zlib_stream.avail_out < sizeof(kGZipHeader)) |  | 
|  137         return Z_BUF_ERROR; |  | 
|  138       memcpy(zlib_stream.next_out, kGZipHeader, sizeof(kGZipHeader)); |  | 
|  139       zlib_stream.next_out += sizeof(kGZipHeader); |  | 
|  140       zlib_stream.avail_out -= sizeof(kGZipHeader); |  | 
|  141     } |  | 
|  142  |  | 
|  143     // Do deflate |  | 
|  144     code = deflate(&zlib_stream, Z_FINISH); |  | 
|  145     *dest_len = *dest_len - zlib_stream.avail_out; |  | 
|  146  |  | 
|  147     deflateEnd(&zlib_stream); |  | 
|  148     return code; |  | 
|  149   } |  | 
|  150  |  | 
|  151   // Use filter to decode compressed data, and compare the decoding result with |  | 
|  152   // the orginal Data. |  | 
|  153   // Parameters: Source and source_len are original data and its size. |  | 
|  154   // Encoded_source and encoded_source_len are compressed data and its size. |  | 
|  155   // Output_buffer_size specifies the size of buffer to read out data from |  | 
|  156   // filter. |  | 
|  157   void DecodeAndCompareWithFilter(Filter* filter, |  | 
|  158                                   const char* source, |  | 
|  159                                   int source_len, |  | 
|  160                                   const char* encoded_source, |  | 
|  161                                   int encoded_source_len, |  | 
|  162                                   int output_buffer_size) { |  | 
|  163     // Make sure we have enough space to hold the decoding output. |  | 
|  164     ASSERT_TRUE(source_len <= kDefaultBufferSize); |  | 
|  165     ASSERT_TRUE(output_buffer_size <= kDefaultBufferSize); |  | 
|  166  |  | 
|  167     char decode_buffer[kDefaultBufferSize]; |  | 
|  168     char* decode_next = decode_buffer; |  | 
|  169     int decode_avail_size = kDefaultBufferSize; |  | 
|  170  |  | 
|  171     const char* encode_next = encoded_source; |  | 
|  172     int encode_avail_size = encoded_source_len; |  | 
|  173  |  | 
|  174     int code = Filter::FILTER_OK; |  | 
|  175     while (code != Filter::FILTER_DONE) { |  | 
|  176       int encode_data_len; |  | 
|  177       encode_data_len = std::min(encode_avail_size, |  | 
|  178                                  filter->stream_buffer_size()); |  | 
|  179       memcpy(filter->stream_buffer()->data(), encode_next, encode_data_len); |  | 
|  180       filter->FlushStreamBuffer(encode_data_len); |  | 
|  181       encode_next += encode_data_len; |  | 
|  182       encode_avail_size -= encode_data_len; |  | 
|  183  |  | 
|  184       while (1) { |  | 
|  185         int decode_data_len = std::min(decode_avail_size, output_buffer_size); |  | 
|  186  |  | 
|  187         code = filter->ReadData(decode_next, &decode_data_len); |  | 
|  188         decode_next += decode_data_len; |  | 
|  189         decode_avail_size -= decode_data_len; |  | 
|  190  |  | 
|  191         ASSERT_TRUE(code != Filter::FILTER_ERROR); |  | 
|  192  |  | 
|  193         if (code == Filter::FILTER_NEED_MORE_DATA || |  | 
|  194             code == Filter::FILTER_DONE) { |  | 
|  195           break; |  | 
|  196         } |  | 
|  197       } |  | 
|  198     } |  | 
|  199  |  | 
|  200     // Compare the decoding result with source data |  | 
|  201     int decode_total_data_len = kDefaultBufferSize - decode_avail_size; |  | 
|  202     EXPECT_TRUE(decode_total_data_len == source_len); |  | 
|  203     EXPECT_EQ(memcmp(source, decode_buffer, source_len), 0); |  | 
|  204   } |  | 
|  205  |  | 
|  206   // Unsafe function to use filter to decode compressed data. |  | 
|  207   // Parameters: Source and source_len are compressed data and its size. |  | 
|  208   // Dest is the buffer for decoding results. Upon entry, *dest_len is the size |  | 
|  209   // of the dest buffer. Upon exit, *dest_len is the number of chars written |  | 
|  210   // into the buffer. |  | 
|  211   int DecodeAllWithFilter(Filter* filter, const char* source, int source_len, |  | 
|  212                           char* dest, int* dest_len) { |  | 
|  213     memcpy(filter->stream_buffer()->data(), source, source_len); |  | 
|  214     filter->FlushStreamBuffer(source_len); |  | 
|  215     return filter->ReadData(dest, dest_len); |  | 
|  216   } |  | 
|  217  |  | 
|  218   void InitFilter(Filter::FilterType type) { |  | 
|  219     std::vector<Filter::FilterType> filter_types; |  | 
|  220     filter_types.push_back(type); |  | 
|  221     filter_ = Filter::Factory(filter_types, filter_context_); |  | 
|  222     ASSERT_TRUE(filter_.get()); |  | 
|  223     ASSERT_GE(filter_->stream_buffer_size(), kDefaultBufferSize); |  | 
|  224   } |  | 
|  225  |  | 
|  226   void InitFilterWithBufferSize(Filter::FilterType type, int buffer_size) { |  | 
|  227     std::vector<Filter::FilterType> filter_types; |  | 
|  228     filter_types.push_back(type); |  | 
|  229     filter_ = |  | 
|  230         Filter::FactoryForTests(filter_types, filter_context_, buffer_size); |  | 
|  231     ASSERT_TRUE(filter_.get()); |  | 
|  232   } |  | 
|  233  |  | 
|  234   const char* source_buffer() const { return source_buffer_.data(); } |  | 
|  235   int source_len() const { return static_cast<int>(source_buffer_.size()); } |  | 
|  236  |  | 
|  237   std::unique_ptr<Filter> filter_; |  | 
|  238  |  | 
|  239   std::string source_buffer_; |  | 
|  240  |  | 
|  241   char* deflate_encode_buffer_; |  | 
|  242   int deflate_encode_len_; |  | 
|  243  |  | 
|  244   char* gzip_encode_buffer_; |  | 
|  245   int gzip_encode_len_; |  | 
|  246  |  | 
|  247  private: |  | 
|  248   MockFilterContext filter_context_; |  | 
|  249 }; |  | 
|  250  |  | 
|  251 // Basic scenario: decoding deflate data with big enough buffer. |  | 
|  252 TEST_F(GZipUnitTest, DecodeDeflate) { |  | 
|  253   // Decode the compressed data with filter |  | 
|  254   InitFilter(Filter::FILTER_TYPE_DEFLATE); |  | 
|  255   memcpy(filter_->stream_buffer()->data(), deflate_encode_buffer_, |  | 
|  256          deflate_encode_len_); |  | 
|  257   filter_->FlushStreamBuffer(deflate_encode_len_); |  | 
|  258  |  | 
|  259   char deflate_decode_buffer[kDefaultBufferSize]; |  | 
|  260   int deflate_decode_size = kDefaultBufferSize; |  | 
|  261   filter_->ReadData(deflate_decode_buffer, &deflate_decode_size); |  | 
|  262  |  | 
|  263   // Compare the decoding result with source data |  | 
|  264   EXPECT_TRUE(deflate_decode_size == source_len()); |  | 
|  265   EXPECT_EQ(memcmp(source_buffer(), deflate_decode_buffer, source_len()), 0); |  | 
|  266 } |  | 
|  267  |  | 
|  268 // Basic scenario: decoding gzip data with big enough buffer. |  | 
|  269 TEST_F(GZipUnitTest, DecodeGZip) { |  | 
|  270   // Decode the compressed data with filter |  | 
|  271   InitFilter(Filter::FILTER_TYPE_GZIP); |  | 
|  272   memcpy(filter_->stream_buffer()->data(), gzip_encode_buffer_, |  | 
|  273          gzip_encode_len_); |  | 
|  274   filter_->FlushStreamBuffer(gzip_encode_len_); |  | 
|  275  |  | 
|  276   char gzip_decode_buffer[kDefaultBufferSize]; |  | 
|  277   int gzip_decode_size = kDefaultBufferSize; |  | 
|  278   filter_->ReadData(gzip_decode_buffer, &gzip_decode_size); |  | 
|  279  |  | 
|  280   // Compare the decoding result with source data |  | 
|  281   EXPECT_TRUE(gzip_decode_size == source_len()); |  | 
|  282   EXPECT_EQ(memcmp(source_buffer(), gzip_decode_buffer, source_len()), 0); |  | 
|  283 } |  | 
|  284  |  | 
|  285 // Tests we can call filter repeatedly to get all the data decoded. |  | 
|  286 // To do that, we create a filter with a small buffer that can not hold all |  | 
|  287 // the input data. |  | 
|  288 TEST_F(GZipUnitTest, DecodeWithSmallBuffer) { |  | 
|  289   InitFilterWithBufferSize(Filter::FILTER_TYPE_DEFLATE, kSmallBufferSize); |  | 
|  290   EXPECT_EQ(kSmallBufferSize, filter_->stream_buffer_size()); |  | 
|  291   DecodeAndCompareWithFilter(filter_.get(), source_buffer(), source_len(), |  | 
|  292                              deflate_encode_buffer_, deflate_encode_len_, |  | 
|  293                              kDefaultBufferSize); |  | 
|  294 } |  | 
|  295  |  | 
|  296 // Tests we can still decode with just 1 byte buffer in the filter. |  | 
|  297 // The purpose of this tests are two: (1) Verify filter can parse partial GZip |  | 
|  298 // header correctly. (2) Sometimes the filter will consume input without |  | 
|  299 // generating output. Verify filter can handle it correctly. |  | 
|  300 TEST_F(GZipUnitTest, DecodeWithOneByteBuffer) { |  | 
|  301   InitFilterWithBufferSize(Filter::FILTER_TYPE_GZIP, 1); |  | 
|  302   EXPECT_EQ(1, filter_->stream_buffer_size()); |  | 
|  303   DecodeAndCompareWithFilter(filter_.get(), source_buffer(), source_len(), |  | 
|  304                              gzip_encode_buffer_, gzip_encode_len_, |  | 
|  305                              kDefaultBufferSize); |  | 
|  306 } |  | 
|  307  |  | 
|  308 // Tests we can decode when caller has small buffer to read out from filter. |  | 
|  309 TEST_F(GZipUnitTest, DecodeWithSmallOutputBuffer) { |  | 
|  310   InitFilter(Filter::FILTER_TYPE_DEFLATE); |  | 
|  311   DecodeAndCompareWithFilter(filter_.get(), source_buffer(), source_len(), |  | 
|  312                              deflate_encode_buffer_, deflate_encode_len_, |  | 
|  313                              kSmallBufferSize); |  | 
|  314 } |  | 
|  315  |  | 
|  316 // Tests we can still decode with just 1 byte buffer in the filter and just 1 |  | 
|  317 // byte buffer in the caller. |  | 
|  318 TEST_F(GZipUnitTest, DecodeWithOneByteInputAndOutputBuffer) { |  | 
|  319   InitFilterWithBufferSize(Filter::FILTER_TYPE_GZIP, 1); |  | 
|  320   EXPECT_EQ(1, filter_->stream_buffer_size()); |  | 
|  321   DecodeAndCompareWithFilter(filter_.get(), source_buffer(), source_len(), |  | 
|  322                              gzip_encode_buffer_, gzip_encode_len_, 1); |  | 
|  323 } |  | 
|  324  |  | 
|  325 // Decoding deflate stream with corrupted data. |  | 
|  326 TEST_F(GZipUnitTest, DecodeCorruptedData) { |  | 
|  327   char corrupt_data[kDefaultBufferSize]; |  | 
|  328   int corrupt_data_len = deflate_encode_len_; |  | 
|  329   memcpy(corrupt_data, deflate_encode_buffer_, deflate_encode_len_); |  | 
|  330  |  | 
|  331   int pos = corrupt_data_len / 2; |  | 
|  332   corrupt_data[pos] = !corrupt_data[pos]; |  | 
|  333  |  | 
|  334   // Decode the corrupted data with filter |  | 
|  335   InitFilter(Filter::FILTER_TYPE_DEFLATE); |  | 
|  336   char corrupt_decode_buffer[kDefaultBufferSize]; |  | 
|  337   int corrupt_decode_size = kDefaultBufferSize; |  | 
|  338  |  | 
|  339   int code = DecodeAllWithFilter(filter_.get(), corrupt_data, corrupt_data_len, |  | 
|  340                                  corrupt_decode_buffer, &corrupt_decode_size); |  | 
|  341  |  | 
|  342   // Expect failures |  | 
|  343   EXPECT_TRUE(code == Filter::FILTER_ERROR); |  | 
|  344 } |  | 
|  345  |  | 
|  346 // Decoding deflate stream with missing data. |  | 
|  347 TEST_F(GZipUnitTest, DecodeMissingData) { |  | 
|  348   char corrupt_data[kDefaultBufferSize]; |  | 
|  349   int corrupt_data_len = deflate_encode_len_; |  | 
|  350   memcpy(corrupt_data, deflate_encode_buffer_, deflate_encode_len_); |  | 
|  351  |  | 
|  352   int pos = corrupt_data_len / 2; |  | 
|  353   int len = corrupt_data_len - pos - 1; |  | 
|  354   memmove(&corrupt_data[pos], &corrupt_data[pos+1], len); |  | 
|  355   --corrupt_data_len; |  | 
|  356  |  | 
|  357   // Decode the corrupted data with filter |  | 
|  358   InitFilter(Filter::FILTER_TYPE_DEFLATE); |  | 
|  359   char corrupt_decode_buffer[kDefaultBufferSize]; |  | 
|  360   int corrupt_decode_size = kDefaultBufferSize; |  | 
|  361  |  | 
|  362   int code = DecodeAllWithFilter(filter_.get(), corrupt_data, corrupt_data_len, |  | 
|  363                                  corrupt_decode_buffer, &corrupt_decode_size); |  | 
|  364  |  | 
|  365   // Expect failures |  | 
|  366   EXPECT_EQ(Filter::FILTER_ERROR, code); |  | 
|  367 } |  | 
|  368  |  | 
|  369 // Decoding gzip stream with corrupted header. |  | 
|  370 TEST_F(GZipUnitTest, DecodeCorruptedHeader) { |  | 
|  371   char corrupt_data[kDefaultBufferSize]; |  | 
|  372   int corrupt_data_len = gzip_encode_len_; |  | 
|  373   memcpy(corrupt_data, gzip_encode_buffer_, gzip_encode_len_); |  | 
|  374  |  | 
|  375   corrupt_data[2] = !corrupt_data[2]; |  | 
|  376  |  | 
|  377   // Decode the corrupted data with filter |  | 
|  378   InitFilter(Filter::FILTER_TYPE_GZIP); |  | 
|  379   char corrupt_decode_buffer[kDefaultBufferSize]; |  | 
|  380   int corrupt_decode_size = kDefaultBufferSize; |  | 
|  381  |  | 
|  382   int code = DecodeAllWithFilter(filter_.get(), corrupt_data, corrupt_data_len, |  | 
|  383                                  corrupt_decode_buffer, &corrupt_decode_size); |  | 
|  384  |  | 
|  385   // Expect failures |  | 
|  386   EXPECT_TRUE(code == Filter::FILTER_ERROR); |  | 
|  387 } |  | 
|  388  |  | 
|  389 }  // namespace net |  | 
| OLD | NEW |