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