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