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