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 <limits.h> 6 7 #include <algorithm> 8 #include <string> 9 #include <vector> 10 11 #include "base/logging.h" 12 #include "base/memory/scoped_ptr.h" 13 #include "net/base/io_buffer.h" 14 #include "net/filter/mock_filter_context.h" 15 #include "net/filter/sdch_filter.h" 16 #include "net/url_request/url_request_context.h" 17 #include "net/url_request/url_request_http_job.h" 18 #include "testing/gtest/include/gtest/gtest.h" 19 #include "third_party/zlib/zlib.h" 20 21 namespace net { 22 23 //------------------------------------------------------------------------------ 24 // Provide sample data and compression results with a sample VCDIFF dictionary. 25 // Note an SDCH dictionary has extra meta-data before the VCDIFF dictionary. 26 static const char kTestVcdiffDictionary[] = "DictionaryFor" 27 "SdchCompression1SdchCompression2SdchCompression3SdchCompression\n"; 28 // Pre-compression test data. Note that we pad with a lot of highly gzip 29 // compressible content to help to exercise the chaining pipeline. That is why 30 // there are a PILE of zeros at the start and end. 31 // This will ensure that gzip compressed data can be fed to the chain in one 32 // gulp, but (with careful selection of intermediate buffers) that it takes 33 // several sdch buffers worth of data to satisfy the sdch filter. See detailed 34 // CHECK() calls in FilterChaining test for specifics. 35 static const char kTestData[] = "0000000000000000000000000000000000000000000000" 36 "0000000000000000000000000000TestData " 37 "SdchCompression1SdchCompression2SdchCompression3SdchCompression" 38 "00000000000000000000000000000000000000000000000000000000000000000000000000" 39 "000000000000000000000000000000000000000\n"; 40 41 // Note SDCH compressed data will include a reference to the SDCH dictionary. 42 static const char kSdchCompressedTestData[] = 43 "\326\303\304\0\0\001M\0\201S\202\004\0\201E\006\001" 44 "00000000000000000000000000000000000000000000000000000000000000000000000000" 45 "TestData 00000000000000000000000000000000000000000000000000000000000000000" 46 "000000000000000000000000000000000000000000000000\n\001S\023\077\001r\r"; 47 48 //------------------------------------------------------------------------------ 49 50 class SdchFilterTest : public testing::Test { 51 protected: 52 SdchFilterTest() 53 : test_vcdiff_dictionary_(kTestVcdiffDictionary, 54 sizeof(kTestVcdiffDictionary) - 1), 55 vcdiff_compressed_data_(kSdchCompressedTestData, 56 sizeof(kSdchCompressedTestData) - 1), 57 expanded_(kTestData, sizeof(kTestData) - 1), 58 sdch_manager_(new SdchManager), 59 filter_context_(new MockFilterContext) { 60 URLRequestContext* url_request_context = 61 filter_context_->GetModifiableURLRequestContext(); 62 63 url_request_context->set_sdch_manager(sdch_manager_.get()); 64 } 65 66 MockFilterContext* filter_context() { return filter_context_.get(); } 67 68 std::string NewSdchCompressedData(const std::string dictionary); 69 70 const std::string test_vcdiff_dictionary_; 71 const std::string vcdiff_compressed_data_; 72 const std::string expanded_; // Desired final, decompressed data. 73 74 scoped_ptr<SdchManager> sdch_manager_; 75 scoped_ptr<MockFilterContext> filter_context_; 76 }; 77 78 std::string SdchFilterTest::NewSdchCompressedData( 79 const std::string dictionary) { 80 std::string client_hash; 81 std::string server_hash; 82 SdchManager::GenerateHash(dictionary, &client_hash, &server_hash); 83 84 // Build compressed data that refers to our dictionary. 85 std::string compressed(server_hash); 86 compressed.append("\0", 1); 87 compressed.append(vcdiff_compressed_data_); 88 return compressed; 89 } 90 91 //------------------------------------------------------------------------------ 92 93 94 TEST_F(SdchFilterTest, Hashing) { 95 std::string client_hash, server_hash; 96 std::string dictionary("test contents"); 97 SdchManager::GenerateHash(dictionary, &client_hash, &server_hash); 98 99 EXPECT_EQ(client_hash, "lMQBjS3P"); 100 EXPECT_EQ(server_hash, "MyciMVll"); 101 } 102 103 104 //------------------------------------------------------------------------------ 105 // Provide a generic helper function for trying to filter data. 106 // This function repeatedly calls the filter to process data, until the entire 107 // source is consumed. The return value from the filter is appended to output. 108 // This allows us to vary input and output block sizes in order to test for edge 109 // effects (boundary effects?) during the filtering process. 110 // This function provides data to the filter in blocks of no-more-than the 111 // specified input_block_length. It allows the filter to fill no more than 112 // output_buffer_length in any one call to proccess (a.k.a., Read) data, and 113 // concatenates all these little output blocks into the singular output string. 114 static bool FilterTestData(const std::string& source, 115 size_t input_block_length, 116 const size_t output_buffer_length, 117 Filter* filter, std::string* output) { 118 CHECK_GT(input_block_length, 0u); 119 Filter::FilterStatus status(Filter::FILTER_NEED_MORE_DATA); 120 size_t source_index = 0; 121 scoped_ptr<char[]> output_buffer(new char[output_buffer_length]); 122 size_t input_amount = std::min(input_block_length, 123 static_cast<size_t>(filter->stream_buffer_size())); 124 125 do { 126 int copy_amount = std::min(input_amount, source.size() - source_index); 127 if (copy_amount > 0 && status == Filter::FILTER_NEED_MORE_DATA) { 128 memcpy(filter->stream_buffer()->data(), source.data() + source_index, 129 copy_amount); 130 filter->FlushStreamBuffer(copy_amount); 131 source_index += copy_amount; 132 } 133 int buffer_length = output_buffer_length; 134 status = filter->ReadData(output_buffer.get(), &buffer_length); 135 output->append(output_buffer.get(), buffer_length); 136 if (status == Filter::FILTER_ERROR) 137 return false; 138 // Callers assume that FILTER_OK with no output buffer means FILTER_DONE. 139 if (Filter::FILTER_OK == status && 0 == buffer_length) 140 return true; 141 if (copy_amount == 0 && buffer_length == 0) 142 return true; 143 } while (1); 144 } 145 //------------------------------------------------------------------------------ 146 static std::string NewSdchDictionary(const std::string& domain) { 147 std::string dictionary; 148 if (!domain.empty()) { 149 dictionary.append("Domain: "); 150 dictionary.append(domain); 151 dictionary.append("\n"); 152 } 153 dictionary.append("\n"); 154 dictionary.append(kTestVcdiffDictionary, sizeof(kTestVcdiffDictionary) - 1); 155 return dictionary; 156 } 157 158 //------------------------------------------------------------------------------ 159 160 TEST_F(SdchFilterTest, EmptyInputOk) { 161 std::vector<Filter::FilterType> filter_types; 162 filter_types.push_back(Filter::FILTER_TYPE_SDCH); 163 char output_buffer[20]; 164 std::string url_string("http://ignore.com"); 165 filter_context()->SetURL(GURL(url_string)); 166 scoped_ptr<Filter> filter(Filter::Factory(filter_types, *filter_context())); 167 168 169 // With no input data, try to read output. 170 int output_bytes_or_buffer_size = sizeof(output_buffer); 171 Filter::FilterStatus status = filter->ReadData(output_buffer, 172 &output_bytes_or_buffer_size); 173 174 EXPECT_EQ(0, output_bytes_or_buffer_size); 175 EXPECT_EQ(Filter::FILTER_NEED_MORE_DATA, status); 176 } 177 178 TEST_F(SdchFilterTest, PassThroughWhenTentative) { 179 std::vector<Filter::FilterType> filter_types; 180 // Selective a tentative filter (which can fall back to pass through). 181 filter_types.push_back(Filter::FILTER_TYPE_GZIP_HELPING_SDCH); 182 char output_buffer[20]; 183 // Response code needs to be 200 to allow a pass through. 184 filter_context()->SetResponseCode(200); 185 std::string url_string("http://ignore.com"); 186 filter_context()->SetURL(GURL(url_string)); 187 scoped_ptr<Filter> filter(Filter::Factory(filter_types, *filter_context())); 188 189 // Supply enough data to force a pass-through mode.. 190 std::string non_gzip_content("not GZIPed data"); 191 192 char* input_buffer = filter->stream_buffer()->data(); 193 int input_buffer_size = filter->stream_buffer_size(); 194 195 EXPECT_LT(static_cast<int>(non_gzip_content.size()), 196 input_buffer_size); 197 memcpy(input_buffer, non_gzip_content.data(), 198 non_gzip_content.size()); 199 filter->FlushStreamBuffer(non_gzip_content.size()); 200 201 // Try to read output. 202 int output_bytes_or_buffer_size = sizeof(output_buffer); 203 Filter::FilterStatus status = filter->ReadData(output_buffer, 204 &output_bytes_or_buffer_size); 205 206 EXPECT_EQ(non_gzip_content.size(), 207 static_cast<size_t>(output_bytes_or_buffer_size)); 208 ASSERT_GT(sizeof(output_buffer), 209 static_cast<size_t>(output_bytes_or_buffer_size)); 210 output_buffer[output_bytes_or_buffer_size] = '\0'; 211 EXPECT_TRUE(non_gzip_content == output_buffer); 212 EXPECT_EQ(Filter::FILTER_NEED_MORE_DATA, status); 213 } 214 215 TEST_F(SdchFilterTest, RefreshBadReturnCode) { 216 std::vector<Filter::FilterType> filter_types; 217 // Selective a tentative filter (which can fall back to pass through). 218 filter_types.push_back(Filter::FILTER_TYPE_SDCH_POSSIBLE); 219 char output_buffer[20]; 220 // Response code needs to be 200 to allow a pass through. 221 filter_context()->SetResponseCode(403); 222 // Meta refresh will only appear for html content 223 filter_context()->SetMimeType("text/html"); 224 std::string url_string("http://ignore.com"); 225 filter_context()->SetURL(GURL(url_string)); 226 scoped_ptr<Filter> filter(Filter::Factory(filter_types, *filter_context())); 227 228 // Supply enough data to force a pass-through mode, which means we have 229 // provided more than 9 characters that can't be a dictionary hash. 230 std::string non_sdch_content("This is not SDCH"); 231 232 char* input_buffer = filter->stream_buffer()->data(); 233 int input_buffer_size = filter->stream_buffer_size(); 234 235 EXPECT_LT(static_cast<int>(non_sdch_content.size()), 236 input_buffer_size); 237 memcpy(input_buffer, non_sdch_content.data(), 238 non_sdch_content.size()); 239 filter->FlushStreamBuffer(non_sdch_content.size()); 240 241 // Try to read output. 242 int output_bytes_or_buffer_size = sizeof(output_buffer); 243 Filter::FilterStatus status = filter->ReadData(output_buffer, 244 &output_bytes_or_buffer_size); 245 246 // We should have read a long and complicated meta-refresh request. 247 EXPECT_TRUE(sizeof(output_buffer) == output_bytes_or_buffer_size); 248 // Check at least the prefix of the return. 249 EXPECT_EQ(0, strncmp(output_buffer, 250 "<head><META HTTP-EQUIV=\"Refresh\" CONTENT=\"0\"></head>", 251 sizeof(output_buffer))); 252 EXPECT_EQ(Filter::FILTER_OK, status); 253 } 254 255 TEST_F(SdchFilterTest, ErrorOnBadReturnCode) { 256 std::vector<Filter::FilterType> filter_types; 257 // Selective a tentative filter (which can fall back to pass through). 258 filter_types.push_back(Filter::FILTER_TYPE_SDCH_POSSIBLE); 259 char output_buffer[20]; 260 // Response code needs to be 200 to allow a pass through. 261 filter_context()->SetResponseCode(403); 262 // Meta refresh will only appear for html content, so set to something else 263 // to induce an error (we can't meta refresh). 264 filter_context()->SetMimeType("anything"); 265 std::string url_string("http://ignore.com"); 266 filter_context()->SetURL(GURL(url_string)); 267 scoped_ptr<Filter> filter(Filter::Factory(filter_types, *filter_context())); 268 269 // Supply enough data to force a pass-through mode, which means we have 270 // provided more than 9 characters that can't be a dictionary hash. 271 std::string non_sdch_content("This is not SDCH"); 272 273 char* input_buffer = filter->stream_buffer()->data(); 274 int input_buffer_size = filter->stream_buffer_size(); 275 276 EXPECT_LT(static_cast<int>(non_sdch_content.size()), 277 input_buffer_size); 278 memcpy(input_buffer, non_sdch_content.data(), 279 non_sdch_content.size()); 280 filter->FlushStreamBuffer(non_sdch_content.size()); 281 282 // Try to read output. 283 int output_bytes_or_buffer_size = sizeof(output_buffer); 284 Filter::FilterStatus status = filter->ReadData(output_buffer, 285 &output_bytes_or_buffer_size); 286 287 EXPECT_EQ(0, output_bytes_or_buffer_size); 288 EXPECT_EQ(Filter::FILTER_ERROR, status); 289 } 290 291 TEST_F(SdchFilterTest, ErrorOnBadReturnCodeWithHtml) { 292 std::vector<Filter::FilterType> filter_types; 293 // Selective a tentative filter (which can fall back to pass through). 294 filter_types.push_back(Filter::FILTER_TYPE_SDCH_POSSIBLE); 295 char output_buffer[20]; 296 // Response code needs to be 200 to allow a pass through. 297 filter_context()->SetResponseCode(403); 298 // Meta refresh will only appear for html content 299 filter_context()->SetMimeType("text/html"); 300 std::string url_string("http://ignore.com"); 301 filter_context()->SetURL(GURL(url_string)); 302 scoped_ptr<Filter> filter(Filter::Factory(filter_types, *filter_context())); 303 304 // Supply enough data to force a pass-through mode, which means we have 305 // provided more than 9 characters that can't be a dictionary hash. 306 std::string non_sdch_content("This is not SDCH"); 307 308 char* input_buffer = filter->stream_buffer()->data(); 309 int input_buffer_size = filter->stream_buffer_size(); 310 311 EXPECT_LT(static_cast<int>(non_sdch_content.size()), 312 input_buffer_size); 313 memcpy(input_buffer, non_sdch_content.data(), 314 non_sdch_content.size()); 315 filter->FlushStreamBuffer(non_sdch_content.size()); 316 317 // Try to read output. 318 int output_bytes_or_buffer_size = sizeof(output_buffer); 319 Filter::FilterStatus status = filter->ReadData(output_buffer, 320 &output_bytes_or_buffer_size); 321 322 // We should have read a long and complicated meta-refresh request. 323 EXPECT_EQ(sizeof(output_buffer), 324 static_cast<size_t>(output_bytes_or_buffer_size)); 325 // Check at least the prefix of the return. 326 EXPECT_EQ(0, strncmp(output_buffer, 327 "<head><META HTTP-EQUIV=\"Refresh\" CONTENT=\"0\"></head>", 328 sizeof(output_buffer))); 329 EXPECT_EQ(Filter::FILTER_OK, status); 330 } 331 332 TEST_F(SdchFilterTest, BasicBadDictionary) { 333 std::vector<Filter::FilterType> filter_types; 334 filter_types.push_back(Filter::FILTER_TYPE_SDCH); 335 char output_buffer[20]; 336 std::string url_string("http://ignore.com"); 337 filter_context()->SetURL(GURL(url_string)); 338 scoped_ptr<Filter> filter(Filter::Factory(filter_types, *filter_context())); 339 340 // Supply bogus data (which doesn't yet specify a full dictionary hash). 341 // Dictionary hash is 8 characters followed by a null. 342 std::string dictionary_hash_prefix("123"); 343 344 char* input_buffer = filter->stream_buffer()->data(); 345 int input_buffer_size = filter->stream_buffer_size(); 346 347 EXPECT_LT(static_cast<int>(dictionary_hash_prefix.size()), 348 input_buffer_size); 349 memcpy(input_buffer, dictionary_hash_prefix.data(), 350 dictionary_hash_prefix.size()); 351 filter->FlushStreamBuffer(dictionary_hash_prefix.size()); 352 353 // With less than a dictionary specifier, try to read output. 354 int output_bytes_or_buffer_size = sizeof(output_buffer); 355 Filter::FilterStatus status = filter->ReadData(output_buffer, 356 &output_bytes_or_buffer_size); 357 358 EXPECT_EQ(0, output_bytes_or_buffer_size); 359 EXPECT_EQ(Filter::FILTER_NEED_MORE_DATA, status); 360 361 // Provide enough data to complete *a* hash, but it is bogus, and not in our 362 // list of dictionaries, so the filter should error out immediately. 363 std::string dictionary_hash_postfix("4abcd\0", 6); 364 365 CHECK_LT(dictionary_hash_postfix.size(), 366 static_cast<size_t>(input_buffer_size)); 367 memcpy(input_buffer, dictionary_hash_postfix.data(), 368 dictionary_hash_postfix.size()); 369 filter->FlushStreamBuffer(dictionary_hash_postfix.size()); 370 371 // With a non-existant dictionary specifier, try to read output. 372 output_bytes_or_buffer_size = sizeof(output_buffer); 373 status = filter->ReadData(output_buffer, &output_bytes_or_buffer_size); 374 375 EXPECT_EQ(0, output_bytes_or_buffer_size); 376 EXPECT_EQ(Filter::FILTER_ERROR, status); 377 378 EXPECT_FALSE(sdch_manager_->IsInSupportedDomain(GURL(url_string))); 379 sdch_manager_->ClearBlacklistings(); 380 EXPECT_TRUE(sdch_manager_->IsInSupportedDomain(GURL(url_string))); 381 } 382 383 TEST_F(SdchFilterTest, DictionaryAddOnce) { 384 // Construct a valid SDCH dictionary from a VCDIFF dictionary. 385 const std::string kSampleDomain = "sdchtest.com"; 386 std::string dictionary(NewSdchDictionary(kSampleDomain)); 387 388 std::string url_string = "http://" + kSampleDomain; 389 GURL url(url_string); 390 EXPECT_TRUE(sdch_manager_->AddSdchDictionary(dictionary, url)); 391 392 // Check we can't add it twice. 393 EXPECT_FALSE(sdch_manager_->AddSdchDictionary(dictionary, url)); 394 395 const std::string kSampleDomain2 = "sdchtest2.com"; 396 397 // Don't test adding a second dictionary if our limits are tight. 398 if (SdchManager::kMaxDictionaryCount > 1) { 399 // Construct a second SDCH dictionary from a VCDIFF dictionary. 400 std::string dictionary2(NewSdchDictionary(kSampleDomain2)); 401 402 std::string url_string2 = "http://" + kSampleDomain2; 403 GURL url2(url_string2); 404 EXPECT_TRUE(sdch_manager_->AddSdchDictionary(dictionary2, url2)); 405 } 406 } 407 408 TEST_F(SdchFilterTest, BasicDictionary) { 409 // Construct a valid SDCH dictionary from a VCDIFF dictionary. 410 const std::string kSampleDomain = "sdchtest.com"; 411 std::string dictionary(NewSdchDictionary(kSampleDomain)); 412 413 std::string url_string = "http://" + kSampleDomain; 414 415 GURL url(url_string); 416 EXPECT_TRUE(sdch_manager_->AddSdchDictionary(dictionary, url)); 417 418 std::string compressed(NewSdchCompressedData(dictionary)); 419 420 std::vector<Filter::FilterType> filter_types; 421 filter_types.push_back(Filter::FILTER_TYPE_SDCH); 422 423 filter_context()->SetURL(url); 424 425 scoped_ptr<Filter> filter(Filter::Factory(filter_types, *filter_context())); 426 427 size_t feed_block_size = 100; 428 size_t output_block_size = 100; 429 std::string output; 430 EXPECT_TRUE(FilterTestData(compressed, feed_block_size, output_block_size, 431 filter.get(), &output)); 432 EXPECT_EQ(output, expanded_); 433 434 // Decode with really small buffers (size 1) to check for edge effects. 435 filter.reset(Filter::Factory(filter_types, *filter_context())); 436 437 feed_block_size = 1; 438 output_block_size = 1; 439 output.clear(); 440 EXPECT_TRUE(FilterTestData(compressed, feed_block_size, output_block_size, 441 filter.get(), &output)); 442 EXPECT_EQ(output, expanded_); 443 } 444 445 TEST_F(SdchFilterTest, NoDecodeHttps) { 446 // Construct a valid SDCH dictionary from a VCDIFF dictionary. 447 const std::string kSampleDomain = "sdchtest.com"; 448 std::string dictionary(NewSdchDictionary(kSampleDomain)); 449 450 std::string url_string = "http://" + kSampleDomain; 451 452 GURL url(url_string); 453 EXPECT_TRUE(sdch_manager_->AddSdchDictionary(dictionary, url)); 454 455 std::string compressed(NewSdchCompressedData(dictionary)); 456 457 std::vector<Filter::FilterType> filter_types; 458 filter_types.push_back(Filter::FILTER_TYPE_SDCH); 459 460 filter_context()->SetURL(GURL("https://" + kSampleDomain)); 461 scoped_ptr<Filter> filter(Filter::Factory(filter_types, *filter_context())); 462 463 const size_t feed_block_size(100); 464 const size_t output_block_size(100); 465 std::string output; 466 467 EXPECT_FALSE(FilterTestData(compressed, feed_block_size, output_block_size, 468 filter.get(), &output)); 469 } 470 471 // Current failsafe TODO/hack refuses to decode any content that doesn't use 472 // http as the scheme (see use of DICTIONARY_SELECTED_FOR_NON_HTTP). 473 // The following tests this blockage. Note that blacklisting results, so we 474 // we need separate tests for each of these. 475 TEST_F(SdchFilterTest, NoDecodeFtp) { 476 // Construct a valid SDCH dictionary from a VCDIFF dictionary. 477 const std::string kSampleDomain = "sdchtest.com"; 478 std::string dictionary(NewSdchDictionary(kSampleDomain)); 479 480 std::string url_string = "http://" + kSampleDomain; 481 482 GURL url(url_string); 483 EXPECT_TRUE(sdch_manager_->AddSdchDictionary(dictionary, url)); 484 485 std::string compressed(NewSdchCompressedData(dictionary)); 486 487 std::vector<Filter::FilterType> filter_types; 488 filter_types.push_back(Filter::FILTER_TYPE_SDCH); 489 490 filter_context()->SetURL(GURL("ftp://" + kSampleDomain)); 491 scoped_ptr<Filter> filter(Filter::Factory(filter_types, *filter_context())); 492 493 const size_t feed_block_size(100); 494 const size_t output_block_size(100); 495 std::string output; 496 497 EXPECT_FALSE(FilterTestData(compressed, feed_block_size, output_block_size, 498 filter.get(), &output)); 499 } 500 501 TEST_F(SdchFilterTest, NoDecodeFileColon) { 502 // Construct a valid SDCH dictionary from a VCDIFF dictionary. 503 const std::string kSampleDomain = "sdchtest.com"; 504 std::string dictionary(NewSdchDictionary(kSampleDomain)); 505 506 std::string url_string = "http://" + kSampleDomain; 507 508 GURL url(url_string); 509 EXPECT_TRUE(sdch_manager_->AddSdchDictionary(dictionary, url)); 510 511 std::string compressed(NewSdchCompressedData(dictionary)); 512 513 std::vector<Filter::FilterType> filter_types; 514 filter_types.push_back(Filter::FILTER_TYPE_SDCH); 515 516 filter_context()->SetURL(GURL("file://" + kSampleDomain)); 517 scoped_ptr<Filter> filter(Filter::Factory(filter_types, *filter_context())); 518 519 const size_t feed_block_size(100); 520 const size_t output_block_size(100); 521 std::string output; 522 523 EXPECT_FALSE(FilterTestData(compressed, feed_block_size, output_block_size, 524 filter.get(), &output)); 525 } 526 527 TEST_F(SdchFilterTest, NoDecodeAboutColon) { 528 // Construct a valid SDCH dictionary from a VCDIFF dictionary. 529 const std::string kSampleDomain = "sdchtest.com"; 530 std::string dictionary(NewSdchDictionary(kSampleDomain)); 531 532 std::string url_string = "http://" + kSampleDomain; 533 534 GURL url(url_string); 535 EXPECT_TRUE(sdch_manager_->AddSdchDictionary(dictionary, url)); 536 537 std::string compressed(NewSdchCompressedData(dictionary)); 538 539 std::vector<Filter::FilterType> filter_types; 540 filter_types.push_back(Filter::FILTER_TYPE_SDCH); 541 542 filter_context()->SetURL(GURL("about://" + kSampleDomain)); 543 scoped_ptr<Filter> filter(Filter::Factory(filter_types, *filter_context())); 544 545 const size_t feed_block_size(100); 546 const size_t output_block_size(100); 547 std::string output; 548 549 EXPECT_FALSE(FilterTestData(compressed, feed_block_size, output_block_size, 550 filter.get(), &output)); 551 } 552 553 TEST_F(SdchFilterTest, NoDecodeJavaScript) { 554 // Construct a valid SDCH dictionary from a VCDIFF dictionary. 555 const std::string kSampleDomain = "sdchtest.com"; 556 std::string dictionary(NewSdchDictionary(kSampleDomain)); 557 558 std::string url_string = "http://" + kSampleDomain; 559 560 GURL url(url_string); 561 EXPECT_TRUE(sdch_manager_->AddSdchDictionary(dictionary, url)); 562 563 std::string compressed(NewSdchCompressedData(dictionary)); 564 565 std::vector<Filter::FilterType> filter_types; 566 filter_types.push_back(Filter::FILTER_TYPE_SDCH); 567 568 filter_context()->SetURL(GURL("javascript://" + kSampleDomain)); 569 scoped_ptr<Filter> filter(Filter::Factory(filter_types, *filter_context())); 570 571 const size_t feed_block_size(100); 572 const size_t output_block_size(100); 573 std::string output; 574 575 EXPECT_FALSE(FilterTestData(compressed, feed_block_size, output_block_size, 576 filter.get(), &output)); 577 } 578 579 TEST_F(SdchFilterTest, CanStillDecodeHttp) { 580 // Construct a valid SDCH dictionary from a VCDIFF dictionary. 581 const std::string kSampleDomain = "sdchtest.com"; 582 std::string dictionary(NewSdchDictionary(kSampleDomain)); 583 584 std::string url_string = "http://" + kSampleDomain; 585 586 GURL url(url_string); 587 EXPECT_TRUE(sdch_manager_->AddSdchDictionary(dictionary, url)); 588 589 std::string compressed(NewSdchCompressedData(dictionary)); 590 591 std::vector<Filter::FilterType> filter_types; 592 filter_types.push_back(Filter::FILTER_TYPE_SDCH); 593 594 filter_context()->SetURL(GURL("http://" + kSampleDomain)); 595 scoped_ptr<Filter> filter(Filter::Factory(filter_types, *filter_context())); 596 597 const size_t feed_block_size(100); 598 const size_t output_block_size(100); 599 std::string output; 600 601 EXPECT_TRUE(FilterTestData(compressed, feed_block_size, output_block_size, 602 filter.get(), &output)); 603 } 604 605 TEST_F(SdchFilterTest, CrossDomainDictionaryUse) { 606 // Construct a valid SDCH dictionary from a VCDIFF dictionary. 607 const std::string kSampleDomain = "sdchtest.com"; 608 std::string dictionary(NewSdchDictionary(kSampleDomain)); 609 610 std::string url_string = "http://" + kSampleDomain; 611 612 GURL url(url_string); 613 EXPECT_TRUE(sdch_manager_->AddSdchDictionary(dictionary, url)); 614 615 std::string compressed(NewSdchCompressedData(dictionary)); 616 617 std::vector<Filter::FilterType> filter_types; 618 filter_types.push_back(Filter::FILTER_TYPE_SDCH); 619 620 // Decode with content arriving from the "wrong" domain. 621 // This tests SdchManager::CanSet(). 622 GURL wrong_domain_url("http://www.wrongdomain.com"); 623 filter_context()->SetURL(wrong_domain_url); 624 scoped_ptr<Filter> filter(Filter::Factory(filter_types, *filter_context())); 625 626 size_t feed_block_size = 100; 627 size_t output_block_size = 100; 628 std::string output; 629 EXPECT_FALSE(FilterTestData(compressed, feed_block_size, output_block_size, 630 filter.get(), &output)); 631 EXPECT_EQ(output.size(), 0u); // No output written. 632 633 EXPECT_TRUE(sdch_manager_->IsInSupportedDomain(GURL(url_string))); 634 EXPECT_FALSE(sdch_manager_->IsInSupportedDomain(wrong_domain_url)); 635 sdch_manager_->ClearBlacklistings(); 636 EXPECT_TRUE(sdch_manager_->IsInSupportedDomain(wrong_domain_url)); 637 } 638 639 TEST_F(SdchFilterTest, DictionaryPathValidation) { 640 // Can't test path distinction between dictionaries if we aren't allowed 641 // more than one dictionary. 642 if (SdchManager::kMaxDictionaryCount <= 1) 643 return; 644 645 // Construct a valid SDCH dictionary from a VCDIFF dictionary. 646 const std::string kSampleDomain = "sdchtest.com"; 647 std::string dictionary(NewSdchDictionary(kSampleDomain)); 648 649 std::string url_string = "http://" + kSampleDomain; 650 651 GURL url(url_string); 652 EXPECT_TRUE(sdch_manager_->AddSdchDictionary(dictionary, url)); 653 654 // Create a dictionary with a path restriction, by prefixing dictionary. 655 const std::string path("/special_path/bin"); 656 std::string dictionary_with_path("Path: " + path + "\n"); 657 dictionary_with_path.append(dictionary); 658 EXPECT_TRUE(sdch_manager_->AddSdchDictionary(dictionary_with_path, url)); 659 660 std::string compressed_for_path(NewSdchCompressedData(dictionary_with_path)); 661 662 std::vector<Filter::FilterType> filter_types; 663 filter_types.push_back(Filter::FILTER_TYPE_SDCH); 664 665 // Test decode the path data, arriving from a valid path. 666 filter_context()->SetURL(GURL(url_string + path)); 667 scoped_ptr<Filter> filter(Filter::Factory(filter_types, *filter_context())); 668 669 size_t feed_block_size = 100; 670 size_t output_block_size = 100; 671 std::string output; 672 673 EXPECT_TRUE(FilterTestData(compressed_for_path, feed_block_size, 674 output_block_size, filter.get(), &output)); 675 EXPECT_EQ(output, expanded_); 676 677 // Test decode the path data, arriving from a invalid path. 678 filter_context()->SetURL(GURL(url_string)); 679 filter.reset(Filter::Factory(filter_types, *filter_context())); 680 681 feed_block_size = 100; 682 output_block_size = 100; 683 output.clear(); 684 EXPECT_FALSE(FilterTestData(compressed_for_path, feed_block_size, 685 output_block_size, filter.get(), &output)); 686 EXPECT_EQ(output.size(), 0u); // No output written. 687 688 EXPECT_FALSE(sdch_manager_->IsInSupportedDomain(GURL(url_string))); 689 sdch_manager_->ClearBlacklistings(); 690 EXPECT_TRUE(sdch_manager_->IsInSupportedDomain(GURL(url_string))); 691 } 692 693 TEST_F(SdchFilterTest, DictionaryPortValidation) { 694 // Can't test port distinction between dictionaries if we aren't allowed 695 // more than one dictionary. 696 if (SdchManager::kMaxDictionaryCount <= 1) 697 return; 698 699 // Construct a valid SDCH dictionary from a VCDIFF dictionary. 700 const std::string kSampleDomain = "sdchtest.com"; 701 std::string dictionary(NewSdchDictionary(kSampleDomain)); 702 703 std::string url_string = "http://" + kSampleDomain; 704 705 GURL url(url_string); 706 EXPECT_TRUE(sdch_manager_->AddSdchDictionary(dictionary, url)); 707 708 709 // Create a dictionary with a port restriction, by prefixing old dictionary. 710 const std::string port("502"); 711 std::string dictionary_with_port("Port: " + port + "\n"); 712 dictionary_with_port.append("Port: 80\n"); // Add default port. 713 dictionary_with_port.append(dictionary); 714 EXPECT_TRUE(sdch_manager_->AddSdchDictionary(dictionary_with_port, 715 GURL(url_string + ":" + port))); 716 717 std::string compressed_for_port(NewSdchCompressedData(dictionary_with_port)); 718 719 std::vector<Filter::FilterType> filter_types; 720 filter_types.push_back(Filter::FILTER_TYPE_SDCH); 721 722 // Test decode the port data, arriving from a valid port. 723 filter_context()->SetURL(GURL(url_string + ":" + port)); 724 scoped_ptr<Filter> filter(Filter::Factory(filter_types, *filter_context())); 725 726 size_t feed_block_size = 100; 727 size_t output_block_size = 100; 728 std::string output; 729 EXPECT_TRUE(FilterTestData(compressed_for_port, feed_block_size, 730 output_block_size, filter.get(), &output)); 731 EXPECT_EQ(output, expanded_); 732 733 // Test decode the port data, arriving from a valid (default) port. 734 filter_context()->SetURL(GURL(url_string)); // Default port. 735 filter.reset(Filter::Factory(filter_types, *filter_context())); 736 737 feed_block_size = 100; 738 output_block_size = 100; 739 output.clear(); 740 EXPECT_TRUE(FilterTestData(compressed_for_port, feed_block_size, 741 output_block_size, filter.get(), &output)); 742 EXPECT_EQ(output, expanded_); 743 744 // Test decode the port data, arriving from a invalid port. 745 filter_context()->SetURL(GURL(url_string + ":" + port + "1")); 746 filter.reset(Filter::Factory(filter_types, *filter_context())); 747 748 feed_block_size = 100; 749 output_block_size = 100; 750 output.clear(); 751 EXPECT_FALSE(FilterTestData(compressed_for_port, feed_block_size, 752 output_block_size, filter.get(), &output)); 753 EXPECT_EQ(output.size(), 0u); // No output written. 754 755 EXPECT_FALSE(sdch_manager_->IsInSupportedDomain(GURL(url_string))); 756 sdch_manager_->ClearBlacklistings(); 757 EXPECT_TRUE(sdch_manager_->IsInSupportedDomain(GURL(url_string))); 758 } 759 760 //------------------------------------------------------------------------------ 761 // Helper function to perform gzip compression of data. 762 763 static std::string gzip_compress(const std::string &input) { 764 z_stream zlib_stream; 765 memset(&zlib_stream, 0, sizeof(zlib_stream)); 766 int code; 767 768 // Initialize zlib 769 code = deflateInit2(&zlib_stream, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 770 -MAX_WBITS, 771 8, // DEF_MEM_LEVEL 772 Z_DEFAULT_STRATEGY); 773 774 CHECK_EQ(Z_OK, code); 775 776 // Fill in zlib control block 777 zlib_stream.next_in = bit_cast<Bytef*>(input.data()); 778 zlib_stream.avail_in = input.size(); 779 780 // Assume we can compress into similar buffer (add 100 bytes to be sure). 781 size_t gzip_compressed_length = zlib_stream.avail_in + 100; 782 scoped_ptr<char[]> gzip_compressed(new char[gzip_compressed_length]); 783 zlib_stream.next_out = bit_cast<Bytef*>(gzip_compressed.get()); 784 zlib_stream.avail_out = gzip_compressed_length; 785 786 // The GZIP header (see RFC 1952): 787 // +---+---+---+---+---+---+---+---+---+---+ 788 // |ID1|ID2|CM |FLG| MTIME |XFL|OS | 789 // +---+---+---+---+---+---+---+---+---+---+ 790 // ID1 \037 791 // ID2 \213 792 // CM \010 (compression method == DEFLATE) 793 // FLG \000 (special flags that we do not support) 794 // MTIME Unix format modification time (0 means not available) 795 // XFL 2-4? DEFLATE flags 796 // OS ???? Operating system indicator (255 means unknown) 797 // 798 // Header value we generate: 799 const char kGZipHeader[] = { '\037', '\213', '\010', '\000', '\000', 800 '\000', '\000', '\000', '\002', '\377' }; 801 CHECK_GT(zlib_stream.avail_out, sizeof(kGZipHeader)); 802 memcpy(zlib_stream.next_out, kGZipHeader, sizeof(kGZipHeader)); 803 zlib_stream.next_out += sizeof(kGZipHeader); 804 zlib_stream.avail_out -= sizeof(kGZipHeader); 805 806 // Do deflate 807 code = deflate(&zlib_stream, Z_FINISH); 808 gzip_compressed_length -= zlib_stream.avail_out; 809 std::string compressed(gzip_compressed.get(), gzip_compressed_length); 810 deflateEnd(&zlib_stream); 811 return compressed; 812 } 813 814 //------------------------------------------------------------------------------ 815 816 class SdchFilterChainingTest { 817 public: 818 static Filter* Factory(const std::vector<Filter::FilterType>& types, 819 const FilterContext& context, int size) { 820 return Filter::FactoryForTests(types, context, size); 821 } 822 }; 823 824 // Test that filters can be cascaded (chained) so that the output of one filter 825 // is processed by the next one. This is most critical for SDCH, which is 826 // routinely followed by gzip (during encoding). The filter we'll test for will 827 // do the gzip decoding first, and then decode the SDCH content. 828 TEST_F(SdchFilterTest, FilterChaining) { 829 // Construct a valid SDCH dictionary from a VCDIFF dictionary. 830 const std::string kSampleDomain = "sdchtest.com"; 831 std::string dictionary(NewSdchDictionary(kSampleDomain)); 832 833 std::string url_string = "http://" + kSampleDomain; 834 835 GURL url(url_string); 836 EXPECT_TRUE(sdch_manager_->AddSdchDictionary(dictionary, url)); 837 838 std::string sdch_compressed(NewSdchCompressedData(dictionary)); 839 840 // Use Gzip to compress the sdch sdch_compressed data. 841 std::string gzip_compressed_sdch = gzip_compress(sdch_compressed); 842 843 // Construct a chained filter. 844 std::vector<Filter::FilterType> filter_types; 845 filter_types.push_back(Filter::FILTER_TYPE_SDCH); 846 filter_types.push_back(Filter::FILTER_TYPE_GZIP); 847 848 // First try with a large buffer (larger than test input, or compressed data). 849 const size_t kLargeInputBufferSize(1000); // Used internally in filters. 850 CHECK_GT(kLargeInputBufferSize, gzip_compressed_sdch.size()); 851 CHECK_GT(kLargeInputBufferSize, sdch_compressed.size()); 852 CHECK_GT(kLargeInputBufferSize, expanded_.size()); 853 filter_context()->SetURL(url); 854 scoped_ptr<Filter> filter( 855 SdchFilterChainingTest::Factory(filter_types, *filter_context(), 856 kLargeInputBufferSize)); 857 EXPECT_EQ(static_cast<int>(kLargeInputBufferSize), 858 filter->stream_buffer_size()); 859 860 // Verify that chained filter is waiting for data. 861 char tiny_output_buffer[10]; 862 int tiny_output_size = sizeof(tiny_output_buffer); 863 EXPECT_EQ(Filter::FILTER_NEED_MORE_DATA, 864 filter->ReadData(tiny_output_buffer, &tiny_output_size)); 865 866 // Make chain process all data. 867 size_t feed_block_size = kLargeInputBufferSize; 868 size_t output_block_size = kLargeInputBufferSize; 869 std::string output; 870 EXPECT_TRUE(FilterTestData(gzip_compressed_sdch, feed_block_size, 871 output_block_size, filter.get(), &output)); 872 EXPECT_EQ(output, expanded_); 873 874 // Next try with a mid-sized internal buffer size. 875 const size_t kMidSizedInputBufferSize(100); 876 // Buffer should be big enough to swallow whole gzip content. 877 CHECK_GT(kMidSizedInputBufferSize, gzip_compressed_sdch.size()); 878 // Buffer should be small enough that entire SDCH content can't fit. 879 // We'll go even further, and force the chain to flush the buffer between the 880 // two filters more than once (that is why we multiply by 2). 881 CHECK_LT(kMidSizedInputBufferSize * 2, sdch_compressed.size()); 882 filter_context()->SetURL(url); 883 filter.reset( 884 SdchFilterChainingTest::Factory(filter_types, *filter_context(), 885 kMidSizedInputBufferSize)); 886 EXPECT_EQ(static_cast<int>(kMidSizedInputBufferSize), 887 filter->stream_buffer_size()); 888 889 feed_block_size = kMidSizedInputBufferSize; 890 output_block_size = kMidSizedInputBufferSize; 891 output.clear(); 892 EXPECT_TRUE(FilterTestData(gzip_compressed_sdch, feed_block_size, 893 output_block_size, filter.get(), &output)); 894 EXPECT_EQ(output, expanded_); 895 896 // Next try with a tiny input and output buffer to cover edge effects. 897 filter.reset(SdchFilterChainingTest::Factory(filter_types, *filter_context(), 898 kLargeInputBufferSize)); 899 EXPECT_EQ(static_cast<int>(kLargeInputBufferSize), 900 filter->stream_buffer_size()); 901 902 feed_block_size = 1; 903 output_block_size = 1; 904 output.clear(); 905 EXPECT_TRUE(FilterTestData(gzip_compressed_sdch, feed_block_size, 906 output_block_size, filter.get(), &output)); 907 EXPECT_EQ(output, expanded_); 908 } 909 910 TEST_F(SdchFilterTest, DefaultGzipIfSdch) { 911 // Construct a valid SDCH dictionary from a VCDIFF dictionary. 912 const std::string kSampleDomain = "sdchtest.com"; 913 std::string dictionary(NewSdchDictionary(kSampleDomain)); 914 915 std::string url_string = "http://" + kSampleDomain; 916 917 GURL url(url_string); 918 EXPECT_TRUE(sdch_manager_->AddSdchDictionary(dictionary, url)); 919 920 std::string sdch_compressed(NewSdchCompressedData(dictionary)); 921 922 // Use Gzip to compress the sdch sdch_compressed data. 923 std::string gzip_compressed_sdch = gzip_compress(sdch_compressed); 924 925 // Only claim to have sdch content, but really use the gzipped sdch content. 926 // System should automatically add the missing (optional) gzip. 927 std::vector<Filter::FilterType> filter_types; 928 filter_types.push_back(Filter::FILTER_TYPE_SDCH); 929 930 filter_context()->SetMimeType("anything/mime"); 931 filter_context()->SetSdchResponse(true); 932 Filter::FixupEncodingTypes(*filter_context(), &filter_types); 933 ASSERT_EQ(filter_types.size(), 2u); 934 EXPECT_EQ(filter_types[0], Filter::FILTER_TYPE_SDCH); 935 EXPECT_EQ(filter_types[1], Filter::FILTER_TYPE_GZIP_HELPING_SDCH); 936 937 // First try with a large buffer (larger than test input, or compressed data). 938 filter_context()->SetURL(url); 939 scoped_ptr<Filter> filter(Filter::Factory(filter_types, *filter_context())); 940 941 942 // Verify that chained filter is waiting for data. 943 char tiny_output_buffer[10]; 944 int tiny_output_size = sizeof(tiny_output_buffer); 945 EXPECT_EQ(Filter::FILTER_NEED_MORE_DATA, 946 filter->ReadData(tiny_output_buffer, &tiny_output_size)); 947 948 size_t feed_block_size = 100; 949 size_t output_block_size = 100; 950 std::string output; 951 EXPECT_TRUE(FilterTestData(gzip_compressed_sdch, feed_block_size, 952 output_block_size, filter.get(), &output)); 953 EXPECT_EQ(output, expanded_); 954 955 // Next try with a tiny buffer to cover edge effects. 956 filter.reset(Filter::Factory(filter_types, *filter_context())); 957 958 feed_block_size = 1; 959 output_block_size = 1; 960 output.clear(); 961 EXPECT_TRUE(FilterTestData(gzip_compressed_sdch, feed_block_size, 962 output_block_size, filter.get(), &output)); 963 EXPECT_EQ(output, expanded_); 964 } 965 966 TEST_F(SdchFilterTest, AcceptGzipSdchIfGzip) { 967 // Construct a valid SDCH dictionary from a VCDIFF dictionary. 968 const std::string kSampleDomain = "sdchtest.com"; 969 std::string dictionary(NewSdchDictionary(kSampleDomain)); 970 971 std::string url_string = "http://" + kSampleDomain; 972 973 GURL url(url_string); 974 EXPECT_TRUE(sdch_manager_->AddSdchDictionary(dictionary, url)); 975 976 std::string sdch_compressed(NewSdchCompressedData(dictionary)); 977 978 // Use Gzip to compress the sdch sdch_compressed data. 979 std::string gzip_compressed_sdch = gzip_compress(sdch_compressed); 980 981 // Some proxies strip the content encoding statement down to a mere gzip, but 982 // pass through the original content (with full sdch,gzip encoding). 983 // Only claim to have gzip content, but really use the gzipped sdch content. 984 // System should automatically add the missing (optional) sdch. 985 std::vector<Filter::FilterType> filter_types; 986 filter_types.push_back(Filter::FILTER_TYPE_GZIP); 987 988 filter_context()->SetMimeType("anything/mime"); 989 filter_context()->SetSdchResponse(true); 990 Filter::FixupEncodingTypes(*filter_context(), &filter_types); 991 ASSERT_EQ(filter_types.size(), 3u); 992 EXPECT_EQ(filter_types[0], Filter::FILTER_TYPE_SDCH_POSSIBLE); 993 EXPECT_EQ(filter_types[1], Filter::FILTER_TYPE_GZIP_HELPING_SDCH); 994 EXPECT_EQ(filter_types[2], Filter::FILTER_TYPE_GZIP); 995 996 // First try with a large buffer (larger than test input, or compressed data). 997 filter_context()->SetURL(url); 998 scoped_ptr<Filter> filter(Filter::Factory(filter_types, *filter_context())); 999 1000 1001 // Verify that chained filter is waiting for data. 1002 char tiny_output_buffer[10]; 1003 int tiny_output_size = sizeof(tiny_output_buffer); 1004 EXPECT_EQ(Filter::FILTER_NEED_MORE_DATA, 1005 filter->ReadData(tiny_output_buffer, &tiny_output_size)); 1006 1007 size_t feed_block_size = 100; 1008 size_t output_block_size = 100; 1009 std::string output; 1010 EXPECT_TRUE(FilterTestData(gzip_compressed_sdch, feed_block_size, 1011 output_block_size, filter.get(), &output)); 1012 EXPECT_EQ(output, expanded_); 1013 1014 // Next try with a tiny buffer to cover edge effects. 1015 filter.reset(Filter::Factory(filter_types, *filter_context())); 1016 1017 feed_block_size = 1; 1018 output_block_size = 1; 1019 output.clear(); 1020 EXPECT_TRUE(FilterTestData(gzip_compressed_sdch, feed_block_size, 1021 output_block_size, filter.get(), &output)); 1022 EXPECT_EQ(output, expanded_); 1023 } 1024 1025 TEST_F(SdchFilterTest, DefaultSdchGzipIfEmpty) { 1026 // Construct a valid SDCH dictionary from a VCDIFF dictionary. 1027 const std::string kSampleDomain = "sdchtest.com"; 1028 std::string dictionary(NewSdchDictionary(kSampleDomain)); 1029 1030 std::string url_string = "http://" + kSampleDomain; 1031 1032 GURL url(url_string); 1033 EXPECT_TRUE(sdch_manager_->AddSdchDictionary(dictionary, url)); 1034 1035 std::string sdch_compressed(NewSdchCompressedData(dictionary)); 1036 1037 // Use Gzip to compress the sdch sdch_compressed data. 1038 std::string gzip_compressed_sdch = gzip_compress(sdch_compressed); 1039 1040 // Only claim to have non-encoded content, but really use the gzipped sdch 1041 // content. 1042 // System should automatically add the missing (optional) sdch,gzip. 1043 std::vector<Filter::FilterType> filter_types; 1044 1045 filter_context()->SetMimeType("anything/mime"); 1046 filter_context()->SetSdchResponse(true); 1047 Filter::FixupEncodingTypes(*filter_context(), &filter_types); 1048 ASSERT_EQ(filter_types.size(), 2u); 1049 EXPECT_EQ(filter_types[0], Filter::FILTER_TYPE_SDCH_POSSIBLE); 1050 EXPECT_EQ(filter_types[1], Filter::FILTER_TYPE_GZIP_HELPING_SDCH); 1051 1052 // First try with a large buffer (larger than test input, or compressed data). 1053 filter_context()->SetURL(url); 1054 scoped_ptr<Filter> filter(Filter::Factory(filter_types, *filter_context())); 1055 1056 1057 // Verify that chained filter is waiting for data. 1058 char tiny_output_buffer[10]; 1059 int tiny_output_size = sizeof(tiny_output_buffer); 1060 EXPECT_EQ(Filter::FILTER_NEED_MORE_DATA, 1061 filter->ReadData(tiny_output_buffer, &tiny_output_size)); 1062 1063 size_t feed_block_size = 100; 1064 size_t output_block_size = 100; 1065 std::string output; 1066 EXPECT_TRUE(FilterTestData(gzip_compressed_sdch, feed_block_size, 1067 output_block_size, filter.get(), &output)); 1068 EXPECT_EQ(output, expanded_); 1069 1070 // Next try with a tiny buffer to cover edge effects. 1071 filter.reset(Filter::Factory(filter_types, *filter_context())); 1072 1073 feed_block_size = 1; 1074 output_block_size = 1; 1075 output.clear(); 1076 EXPECT_TRUE(FilterTestData(gzip_compressed_sdch, feed_block_size, 1077 output_block_size, filter.get(), &output)); 1078 EXPECT_EQ(output, expanded_); 1079 } 1080 1081 TEST_F(SdchFilterTest, AcceptGzipGzipSdchIfGzip) { 1082 // Construct a valid SDCH dictionary from a VCDIFF dictionary. 1083 const std::string kSampleDomain = "sdchtest.com"; 1084 std::string dictionary(NewSdchDictionary(kSampleDomain)); 1085 1086 std::string url_string = "http://" + kSampleDomain; 1087 1088 GURL url(url_string); 1089 EXPECT_TRUE(sdch_manager_->AddSdchDictionary(dictionary, url)); 1090 1091 std::string sdch_compressed(NewSdchCompressedData(dictionary)); 1092 1093 // Vodaphone (UK) Mobile Broadband provides double gzipped sdch with a content 1094 // encoding of merely gzip (apparently, only listing the extra level of 1095 // wrapper compression they added, but discarding the actual content encoding. 1096 // Use Gzip to double compress the sdch sdch_compressed data. 1097 std::string double_gzip_compressed_sdch = gzip_compress(gzip_compress( 1098 sdch_compressed)); 1099 1100 // Only claim to have gzip content, but really use the double gzipped sdch 1101 // content. 1102 // System should automatically add the missing (optional) sdch, gzip decoders. 1103 std::vector<Filter::FilterType> filter_types; 1104 filter_types.push_back(Filter::FILTER_TYPE_GZIP); 1105 1106 filter_context()->SetMimeType("anything/mime"); 1107 filter_context()->SetSdchResponse(true); 1108 Filter::FixupEncodingTypes(*filter_context(), &filter_types); 1109 ASSERT_EQ(filter_types.size(), 3u); 1110 EXPECT_EQ(filter_types[0], Filter::FILTER_TYPE_SDCH_POSSIBLE); 1111 EXPECT_EQ(filter_types[1], Filter::FILTER_TYPE_GZIP_HELPING_SDCH); 1112 EXPECT_EQ(filter_types[2], Filter::FILTER_TYPE_GZIP); 1113 1114 // First try with a large buffer (larger than test input, or compressed data). 1115 filter_context()->SetURL(url); 1116 scoped_ptr<Filter> filter(Filter::Factory(filter_types, *filter_context())); 1117 1118 // Verify that chained filter is waiting for data. 1119 char tiny_output_buffer[10]; 1120 int tiny_output_size = sizeof(tiny_output_buffer); 1121 EXPECT_EQ(Filter::FILTER_NEED_MORE_DATA, 1122 filter->ReadData(tiny_output_buffer, &tiny_output_size)); 1123 1124 size_t feed_block_size = 100; 1125 size_t output_block_size = 100; 1126 std::string output; 1127 EXPECT_TRUE(FilterTestData(double_gzip_compressed_sdch, feed_block_size, 1128 output_block_size, filter.get(), &output)); 1129 EXPECT_EQ(output, expanded_); 1130 1131 // Next try with a tiny buffer to cover edge effects. 1132 filter.reset(Filter::Factory(filter_types, *filter_context())); 1133 1134 feed_block_size = 1; 1135 output_block_size = 1; 1136 output.clear(); 1137 EXPECT_TRUE(FilterTestData(double_gzip_compressed_sdch, feed_block_size, 1138 output_block_size, filter.get(), &output)); 1139 EXPECT_EQ(output, expanded_); 1140 } 1141 1142 } // namespace net 1143