• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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:
SetUp()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 
TearDown()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.
CompressAll(EncodeMode mode,const char * source,int source_size,char * dest,int * dest_len)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.
DecodeAndCompareWithFilter(Filter * filter,const char * source,int source_len,const char * encoded_source,int encoded_source_len,int output_buffer_size)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.
DecodeAllWithFilter(Filter * filter,const char * source,int source_len,char * dest,int * dest_len)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 
source_buffer() const229   const char* source_buffer() const { return source_buffer_.data(); }
source_len() const230   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.
TEST_F(GZipUnitTest,DecodeDeflate)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.
TEST_F(GZipUnitTest,DecodeGZip)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.
TEST_F(GZipUnitTest,DecodeWithSmallBuffer)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.
TEST_F(GZipUnitTest,DecodeWithOneByteBuffer)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.
TEST_F(GZipUnitTest,DecodeWithSmallOutputBuffer)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.
TEST_F(GZipUnitTest,DecodeWithOneByteInputAndOutputBuffer)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.
TEST_F(GZipUnitTest,DecodeCorruptedData)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.
TEST_F(GZipUnitTest,DecodeMissingData)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.
TEST_F(GZipUnitTest,DecodeCorruptedHeader)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