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