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