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