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