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