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