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