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