1 // Copyright 2016 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 <string>
6 #include <utility>
7
8 #include "base/bind.h"
9 #include "base/bit_cast.h"
10 #include "base/callback.h"
11 #include "net/base/io_buffer.h"
12 #include "net/base/test_completion_callback.h"
13 #include "net/filter/filter_source_stream_test_util.h"
14 #include "net/filter/gzip_source_stream.h"
15 #include "net/filter/mock_source_stream.h"
16 #include "testing/gtest/include/gtest/gtest.h"
17 #include "third_party/zlib/zlib.h"
18
19 namespace net {
20
21 namespace {
22
23 const int kBigBufferSize = 4096;
24 const int kSmallBufferSize = 1;
25
26 enum class ReadResultType {
27 // Each call to AddReadResult is a separate read from the lower layer
28 // SourceStream.
29 EVERYTHING_AT_ONCE,
30 // Whenever AddReadResult is called, each byte is actually a separate read
31 // result.
32 ONE_BYTE_AT_A_TIME,
33 };
34
35 // How many bytes to leave unused at the end of |source_data_|. This margin is
36 // present so that tests that need to append data after the zlib EOF do not run
37 // out of room in the output buffer.
38 const size_t kEOFMargin = 64;
39
40 struct GzipTestParam {
GzipTestParamnet::__anonbd691ed30111::GzipTestParam41 GzipTestParam(int buf_size,
42 MockSourceStream::Mode read_mode,
43 ReadResultType read_result_type)
44 : buffer_size(buf_size),
45 mode(read_mode),
46 read_result_type(read_result_type) {}
47
48 const int buffer_size;
49 const MockSourceStream::Mode mode;
50 const ReadResultType read_result_type;
51 };
52
53 } // namespace
54
55 class GzipSourceStreamTest : public ::testing::TestWithParam<GzipTestParam> {
56 protected:
GzipSourceStreamTest()57 GzipSourceStreamTest() : output_buffer_size_(GetParam().buffer_size) {}
58
59 // Helpful function to initialize the test fixture.|type| specifies which type
60 // of GzipSourceStream to create. It must be one of TYPE_GZIP and
61 // TYPE_DEFLATE.
Init(SourceStream::SourceType type)62 void Init(SourceStream::SourceType type) {
63 EXPECT_TRUE(SourceStream::TYPE_GZIP == type ||
64 SourceStream::TYPE_DEFLATE == type);
65 source_data_len_ = kBigBufferSize - kEOFMargin;
66
67 for (size_t i = 0; i < source_data_len_; i++)
68 source_data_[i] = i % 256;
69
70 encoded_data_len_ = kBigBufferSize;
71 CompressGzip(source_data_, source_data_len_, encoded_data_,
72 &encoded_data_len_, type != SourceStream::TYPE_DEFLATE);
73
74 output_buffer_ = base::MakeRefCounted<IOBuffer>(output_buffer_size_);
75 std::unique_ptr<MockSourceStream> source(new MockSourceStream());
76 if (GetParam().read_result_type == ReadResultType::ONE_BYTE_AT_A_TIME)
77 source->set_read_one_byte_at_a_time(true);
78 source_ = source.get();
79 stream_ = GzipSourceStream::Create(std::move(source), type);
80 }
81
82 // If MockSourceStream::Mode is ASYNC, completes reads from |mock_stream|
83 // until there's no pending read, and then returns |callback|'s result, once
84 // it's invoked. If Mode is not ASYNC, does nothing and returns
85 // |previous_result|.
CompleteReadsIfAsync(int previous_result,TestCompletionCallback * callback,MockSourceStream * mock_stream)86 int CompleteReadsIfAsync(int previous_result,
87 TestCompletionCallback* callback,
88 MockSourceStream* mock_stream) {
89 if (GetParam().mode == MockSourceStream::ASYNC) {
90 EXPECT_EQ(ERR_IO_PENDING, previous_result);
91 while (mock_stream->awaiting_completion())
92 mock_stream->CompleteNextRead();
93 return callback->WaitForResult();
94 }
95 return previous_result;
96 }
97
source_data()98 char* source_data() { return source_data_; }
source_data_len()99 size_t source_data_len() { return source_data_len_; }
100
encoded_data()101 char* encoded_data() { return encoded_data_; }
encoded_data_len()102 size_t encoded_data_len() { return encoded_data_len_; }
103
output_buffer()104 IOBuffer* output_buffer() { return output_buffer_.get(); }
output_data()105 char* output_data() { return output_buffer_->data(); }
output_buffer_size()106 size_t output_buffer_size() { return output_buffer_size_; }
107
source()108 MockSourceStream* source() { return source_; }
stream()109 GzipSourceStream* stream() { return stream_.get(); }
110
111 // Reads from |stream_| until an error occurs or the EOF is reached.
112 // When an error occurs, returns the net error code. When an EOF is reached,
113 // returns the number of bytes read and appends data read to |output|.
ReadStream(std::string * output)114 int ReadStream(std::string* output) {
115 int bytes_read = 0;
116 while (true) {
117 TestCompletionCallback callback;
118 int rv = stream_->Read(output_buffer(), output_buffer_size(),
119 callback.callback());
120 if (rv == ERR_IO_PENDING)
121 rv = CompleteReadsIfAsync(rv, &callback, source());
122 if (rv == OK)
123 break;
124 if (rv < OK)
125 return rv;
126 EXPECT_GT(rv, OK);
127 bytes_read += rv;
128 output->append(output_data(), rv);
129 }
130 return bytes_read;
131 }
132
133 private:
134 char source_data_[kBigBufferSize];
135 size_t source_data_len_;
136
137 char encoded_data_[kBigBufferSize];
138 size_t encoded_data_len_;
139
140 scoped_refptr<IOBuffer> output_buffer_;
141 const int output_buffer_size_;
142
143 MockSourceStream* source_;
144 std::unique_ptr<GzipSourceStream> stream_;
145 };
146
147 INSTANTIATE_TEST_SUITE_P(
148 GzipSourceStreamTests,
149 GzipSourceStreamTest,
150 ::testing::Values(GzipTestParam(kBigBufferSize,
151 MockSourceStream::SYNC,
152 ReadResultType::EVERYTHING_AT_ONCE),
153 GzipTestParam(kSmallBufferSize,
154 MockSourceStream::SYNC,
155 ReadResultType::EVERYTHING_AT_ONCE),
156 GzipTestParam(kBigBufferSize,
157 MockSourceStream::ASYNC,
158 ReadResultType::EVERYTHING_AT_ONCE),
159 GzipTestParam(kSmallBufferSize,
160 MockSourceStream::ASYNC,
161 ReadResultType::EVERYTHING_AT_ONCE),
162 GzipTestParam(kBigBufferSize,
163 MockSourceStream::SYNC,
164 ReadResultType::ONE_BYTE_AT_A_TIME),
165 GzipTestParam(kSmallBufferSize,
166 MockSourceStream::SYNC,
167 ReadResultType::ONE_BYTE_AT_A_TIME),
168 GzipTestParam(kBigBufferSize,
169 MockSourceStream::ASYNC,
170 ReadResultType::ONE_BYTE_AT_A_TIME),
171 GzipTestParam(kSmallBufferSize,
172 MockSourceStream::ASYNC,
173 ReadResultType::ONE_BYTE_AT_A_TIME)));
174
TEST_P(GzipSourceStreamTest,EmptyStream)175 TEST_P(GzipSourceStreamTest, EmptyStream) {
176 Init(SourceStream::TYPE_DEFLATE);
177 source()->AddReadResult(nullptr, 0, OK, GetParam().mode);
178 TestCompletionCallback callback;
179 std::string actual_output;
180 int result = ReadStream(&actual_output);
181 EXPECT_EQ(OK, result);
182 EXPECT_EQ("DEFLATE", stream()->Description());
183 }
184
TEST_P(GzipSourceStreamTest,DeflateOneBlock)185 TEST_P(GzipSourceStreamTest, DeflateOneBlock) {
186 Init(SourceStream::TYPE_DEFLATE);
187 source()->AddReadResult(encoded_data(), encoded_data_len(), OK,
188 GetParam().mode);
189 source()->AddReadResult(nullptr, 0, OK, GetParam().mode);
190 std::string actual_output;
191 int rv = ReadStream(&actual_output);
192 EXPECT_EQ(static_cast<int>(source_data_len()), rv);
193 EXPECT_EQ(std::string(source_data(), source_data_len()), actual_output);
194 EXPECT_EQ("DEFLATE", stream()->Description());
195 }
196
TEST_P(GzipSourceStreamTest,GzipOneBloc)197 TEST_P(GzipSourceStreamTest, GzipOneBloc) {
198 Init(SourceStream::TYPE_GZIP);
199 source()->AddReadResult(encoded_data(), encoded_data_len(), OK,
200 GetParam().mode);
201 source()->AddReadResult(nullptr, 0, OK, GetParam().mode);
202 std::string actual_output;
203 int rv = ReadStream(&actual_output);
204 EXPECT_EQ(static_cast<int>(source_data_len()), rv);
205 EXPECT_EQ(std::string(source_data(), source_data_len()), actual_output);
206 EXPECT_EQ("GZIP", stream()->Description());
207 }
208
TEST_P(GzipSourceStreamTest,DeflateTwoReads)209 TEST_P(GzipSourceStreamTest, DeflateTwoReads) {
210 Init(SourceStream::TYPE_DEFLATE);
211 source()->AddReadResult(encoded_data(), 10, OK, GetParam().mode);
212 source()->AddReadResult(encoded_data() + 10, encoded_data_len() - 10, OK,
213 GetParam().mode);
214 source()->AddReadResult(nullptr, 0, OK, GetParam().mode);
215 std::string actual_output;
216 int rv = ReadStream(&actual_output);
217 EXPECT_EQ(static_cast<int>(source_data_len()), rv);
218 EXPECT_EQ(std::string(source_data(), source_data_len()), actual_output);
219 EXPECT_EQ("DEFLATE", stream()->Description());
220 }
221
222 // Check that any extra bytes after the end of the gzipped data are silently
223 // ignored.
TEST_P(GzipSourceStreamTest,IgnoreDataAfterEof)224 TEST_P(GzipSourceStreamTest, IgnoreDataAfterEof) {
225 Init(SourceStream::TYPE_DEFLATE);
226 const char kExtraData[] = "Hello, World!";
227 std::string encoded_data_with_trailing_data(encoded_data(),
228 encoded_data_len());
229 encoded_data_with_trailing_data.append(kExtraData, sizeof(kExtraData));
230 source()->AddReadResult(encoded_data_with_trailing_data.c_str(),
231 encoded_data_with_trailing_data.length(), OK,
232 GetParam().mode);
233 source()->AddReadResult(nullptr, 0, OK, GetParam().mode);
234 // Compressed and uncompressed data get returned as separate Read() results,
235 // so this test has to call Read twice.
236 std::string actual_output;
237 int rv = ReadStream(&actual_output);
238 std::string expected_output(source_data(), source_data_len());
239 EXPECT_EQ(static_cast<int>(expected_output.size()), rv);
240 EXPECT_EQ(expected_output, actual_output);
241 EXPECT_EQ("DEFLATE", stream()->Description());
242 }
243
TEST_P(GzipSourceStreamTest,MissingZlibHeader)244 TEST_P(GzipSourceStreamTest, MissingZlibHeader) {
245 Init(SourceStream::TYPE_DEFLATE);
246 const size_t kZlibHeaderLen = 2;
247 source()->AddReadResult(encoded_data() + kZlibHeaderLen,
248 encoded_data_len() - kZlibHeaderLen, OK,
249 GetParam().mode);
250 source()->AddReadResult(nullptr, 0, OK, GetParam().mode);
251 std::string actual_output;
252 int rv = ReadStream(&actual_output);
253 EXPECT_EQ(static_cast<int>(source_data_len()), rv);
254 EXPECT_EQ(std::string(source_data(), source_data_len()), actual_output);
255 EXPECT_EQ("DEFLATE", stream()->Description());
256 }
257
TEST_P(GzipSourceStreamTest,CorruptGzipHeader)258 TEST_P(GzipSourceStreamTest, CorruptGzipHeader) {
259 Init(SourceStream::TYPE_GZIP);
260 encoded_data()[1] = 0;
261 int read_len = encoded_data_len();
262 // Needed to a avoid a DCHECK that all reads were consumed.
263 if (GetParam().read_result_type == ReadResultType::ONE_BYTE_AT_A_TIME)
264 read_len = 2;
265 source()->AddReadResult(encoded_data(), read_len, OK, GetParam().mode);
266 std::string actual_output;
267 int rv = ReadStream(&actual_output);
268 EXPECT_EQ(ERR_CONTENT_DECODING_FAILED, rv);
269 EXPECT_EQ("GZIP", stream()->Description());
270 }
271
272 // This test checks that the gzip stream source works correctly on 'golden' data
273 // as produced by gzip(1).
TEST_P(GzipSourceStreamTest,GzipCorrectness)274 TEST_P(GzipSourceStreamTest, GzipCorrectness) {
275 Init(SourceStream::TYPE_GZIP);
276 const char kDecompressedData[] = "Hello, World!";
277 const unsigned char kGzipData[] = {
278 // From:
279 // echo -n 'Hello, World!' | gzip | xxd -i | sed -e 's/^/ /'
280 // The footer is the last 8 bytes.
281 0x1f, 0x8b, 0x08, 0x00, 0x2b, 0x02, 0x84, 0x55, 0x00, 0x03, 0xf3,
282 0x48, 0xcd, 0xc9, 0xc9, 0xd7, 0x51, 0x08, 0xcf, 0x2f, 0xca, 0x49,
283 0x51, 0x04, 0x00, 0xd0, 0xc3, 0x4a, 0xec, 0x0d, 0x00, 0x00, 0x00};
284 source()->AddReadResult(reinterpret_cast<const char*>(kGzipData),
285 sizeof(kGzipData), OK, GetParam().mode);
286 source()->AddReadResult(nullptr, 0, OK, GetParam().mode);
287 std::string actual_output;
288 int rv = ReadStream(&actual_output);
289 EXPECT_EQ(static_cast<int>(strlen(kDecompressedData)), rv);
290 EXPECT_EQ(kDecompressedData, actual_output);
291 EXPECT_EQ("GZIP", stream()->Description());
292 }
293
294 // Same as GzipCorrectness except that last 8 bytes are removed to test that the
295 // implementation can handle missing footer.
TEST_P(GzipSourceStreamTest,GzipCorrectnessWithoutFooter)296 TEST_P(GzipSourceStreamTest, GzipCorrectnessWithoutFooter) {
297 Init(SourceStream::TYPE_GZIP);
298 const char kDecompressedData[] = "Hello, World!";
299 const unsigned char kGzipData[] = {
300 // From:
301 // echo -n 'Hello, World!' | gzip | xxd -i | sed -e 's/^/ /'
302 // with the 8 footer bytes removed.
303 0x1f, 0x8b, 0x08, 0x00, 0x2b, 0x02, 0x84, 0x55, 0x00,
304 0x03, 0xf3, 0x48, 0xcd, 0xc9, 0xc9, 0xd7, 0x51, 0x08,
305 0xcf, 0x2f, 0xca, 0x49, 0x51, 0x04, 0x00};
306 source()->AddReadResult(reinterpret_cast<const char*>(kGzipData),
307 sizeof(kGzipData), OK, GetParam().mode);
308 source()->AddReadResult(nullptr, 0, OK, GetParam().mode);
309 std::string actual_output;
310 int rv = ReadStream(&actual_output);
311 EXPECT_EQ(static_cast<int>(strlen(kDecompressedData)), rv);
312 EXPECT_EQ(kDecompressedData, actual_output);
313 EXPECT_EQ("GZIP", stream()->Description());
314 }
315
316 // Test with the same compressed data as the above tests, but uses deflate with
317 // header and checksum. Tests the Z_STREAM_END case in
318 // STATE_SNIFFING_DEFLATE_HEADER.
TEST_P(GzipSourceStreamTest,DeflateWithAdler32)319 TEST_P(GzipSourceStreamTest, DeflateWithAdler32) {
320 Init(SourceStream::TYPE_DEFLATE);
321 const char kDecompressedData[] = "Hello, World!";
322 const unsigned char kGzipData[] = {0x78, 0x01, 0xf3, 0x48, 0xcd, 0xc9, 0xc9,
323 0xd7, 0x51, 0x08, 0xcf, 0x2f, 0xca, 0x49,
324 0x51, 0x04, 0x00, 0x1f, 0x9e, 0x04, 0x6a};
325 source()->AddReadResult(reinterpret_cast<const char*>(kGzipData),
326 sizeof(kGzipData), OK, GetParam().mode);
327 source()->AddReadResult(nullptr, 0, OK, GetParam().mode);
328 std::string actual_output;
329 int rv = ReadStream(&actual_output);
330 EXPECT_EQ(static_cast<int>(strlen(kDecompressedData)), rv);
331 EXPECT_EQ(kDecompressedData, actual_output);
332 EXPECT_EQ("DEFLATE", stream()->Description());
333 }
334
TEST_P(GzipSourceStreamTest,DeflateWithBadAdler32)335 TEST_P(GzipSourceStreamTest, DeflateWithBadAdler32) {
336 Init(SourceStream::TYPE_DEFLATE);
337 const unsigned char kGzipData[] = {0x78, 0x01, 0xf3, 0x48, 0xcd, 0xc9, 0xc9,
338 0xd7, 0x51, 0x08, 0xcf, 0x2f, 0xca, 0x49,
339 0x51, 0x04, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
340 source()->AddReadResult(reinterpret_cast<const char*>(kGzipData),
341 sizeof(kGzipData), OK, GetParam().mode);
342 std::string actual_output;
343 int rv = ReadStream(&actual_output);
344 EXPECT_EQ(ERR_CONTENT_DECODING_FAILED, rv);
345 EXPECT_EQ("DEFLATE", stream()->Description());
346 }
347
TEST_P(GzipSourceStreamTest,DeflateWithoutHeaderWithAdler32)348 TEST_P(GzipSourceStreamTest, DeflateWithoutHeaderWithAdler32) {
349 Init(SourceStream::TYPE_DEFLATE);
350 const char kDecompressedData[] = "Hello, World!";
351 const unsigned char kGzipData[] = {0xf3, 0x48, 0xcd, 0xc9, 0xc9, 0xd7, 0x51,
352 0x08, 0xcf, 0x2f, 0xca, 0x49, 0x51, 0x04,
353 0x00, 0x1f, 0x9e, 0x04, 0x6a};
354 source()->AddReadResult(reinterpret_cast<const char*>(kGzipData),
355 sizeof(kGzipData), OK, GetParam().mode);
356 source()->AddReadResult(nullptr, 0, OK, GetParam().mode);
357 std::string actual_output;
358 int rv = ReadStream(&actual_output);
359 EXPECT_EQ(static_cast<int>(strlen(kDecompressedData)), rv);
360 EXPECT_EQ(kDecompressedData, actual_output);
361 EXPECT_EQ("DEFLATE", stream()->Description());
362 }
363
TEST_P(GzipSourceStreamTest,DeflateWithoutHeaderWithBadAdler32)364 TEST_P(GzipSourceStreamTest, DeflateWithoutHeaderWithBadAdler32) {
365 Init(SourceStream::TYPE_DEFLATE);
366 const unsigned char kGzipData[] = {0xf3, 0x48, 0xcd, 0xc9, 0xc9, 0xd7, 0x51,
367 0x08, 0xcf, 0x2f, 0xca, 0x49, 0x51, 0x04,
368 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
369 source()->AddReadResult(reinterpret_cast<const char*>(kGzipData),
370 sizeof(kGzipData), OK, GetParam().mode);
371 std::string actual_output;
372 int rv = ReadStream(&actual_output);
373 EXPECT_EQ(ERR_CONTENT_DECODING_FAILED, rv);
374 EXPECT_EQ("DEFLATE", stream()->Description());
375 }
376
377 } // namespace net
378