| 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 |