1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include <limits.h> 6 7 #include <algorithm> 8 #include <string> 9 #include <vector> 10 11 #if defined(USE_SYSTEM_ZLIB) 12 #include <zlib.h> 13 #else 14 #include "third_party/zlib/zlib.h" 15 #endif 16 17 #include "base/logging.h" 18 #include "base/memory/scoped_ptr.h" 19 #include "net/base/filter.h" 20 #include "net/base/io_buffer.h" 21 #include "net/base/mock_filter_context.h" 22 #include "net/base/sdch_filter.h" 23 #include "net/url_request/url_request_http_job.h" 24 #include "testing/gtest/include/gtest/gtest.h" 25 26 namespace net { 27 28 //------------------------------------------------------------------------------ 29 // Provide sample data and compression results with a sample VCDIFF dictionary. 30 // Note an SDCH dictionary has extra meta-data before the VCDIFF dictionary. 31 static const char kTestVcdiffDictionary[] = "DictionaryFor" 32 "SdchCompression1SdchCompression2SdchCompression3SdchCompression\n"; 33 // Pre-compression test data. Note that we pad with a lot of highly gzip 34 // compressible content to help to exercise the chaining pipeline. That is why 35 // there are a PILE of zeros at the start and end. 36 // This will ensure that gzip compressed data can be fed to the chain in one 37 // gulp, but (with careful selection of intermediate buffers) that it takes 38 // several sdch buffers worth of data to satisfy the sdch filter. See detailed 39 // CHECK() calls in FilterChaining test for specifics. 40 static const char kTestData[] = "0000000000000000000000000000000000000000000000" 41 "0000000000000000000000000000TestData " 42 "SdchCompression1SdchCompression2SdchCompression3SdchCompression" 43 "00000000000000000000000000000000000000000000000000000000000000000000000000" 44 "000000000000000000000000000000000000000\n"; 45 46 // Note SDCH compressed data will include a reference to the SDCH dictionary. 47 static const char kSdchCompressedTestData[] = 48 "\326\303\304\0\0\001M\0\201S\202\004\0\201E\006\001" 49 "00000000000000000000000000000000000000000000000000000000000000000000000000" 50 "TestData 00000000000000000000000000000000000000000000000000000000000000000" 51 "000000000000000000000000000000000000000000000000\n\001S\023\077\001r\r"; 52 53 //------------------------------------------------------------------------------ 54 55 class SdchFilterTest : public testing::Test { 56 protected: 57 SdchFilterTest() 58 : test_vcdiff_dictionary_(kTestVcdiffDictionary, 59 sizeof(kTestVcdiffDictionary) - 1), 60 vcdiff_compressed_data_(kSdchCompressedTestData, 61 sizeof(kSdchCompressedTestData) - 1), 62 expanded_(kTestData, sizeof(kTestData) - 1), 63 sdch_manager_(new SdchManager) { 64 sdch_manager_->EnableSdchSupport(""); 65 } 66 67 std::string NewSdchCompressedData(const std::string dictionary); 68 69 const std::string test_vcdiff_dictionary_; 70 const std::string vcdiff_compressed_data_; 71 const std::string expanded_; // Desired final, decompressed data. 72 73 scoped_ptr<SdchManager> sdch_manager_; // A singleton database. 74 }; 75 76 std::string SdchFilterTest::NewSdchCompressedData( 77 const std::string dictionary) { 78 std::string client_hash; 79 std::string server_hash; 80 SdchManager::GenerateHash(dictionary, &client_hash, &server_hash); 81 82 // Build compressed data that refers to our dictionary. 83 std::string compressed(server_hash); 84 compressed.append("\0", 1); 85 compressed.append(vcdiff_compressed_data_); 86 return compressed; 87 } 88 89 //------------------------------------------------------------------------------ 90 91 92 TEST_F(SdchFilterTest, Hashing) { 93 std::string client_hash, server_hash; 94 std::string dictionary("test contents"); 95 SdchManager::GenerateHash(dictionary, &client_hash, &server_hash); 96 97 EXPECT_EQ(client_hash, "lMQBjS3P"); 98 EXPECT_EQ(server_hash, "MyciMVll"); 99 } 100 101 102 //------------------------------------------------------------------------------ 103 // Provide a generic helper function for trying to filter data. 104 // This function repeatedly calls the filter to process data, until the entire 105 // source is consumed. The return value from the filter is appended to output. 106 // This allows us to vary input and output block sizes in order to test for edge 107 // effects (boundary effects?) during the filtering process. 108 // This function provides data to the filter in blocks of no-more-than the 109 // specified input_block_length. It allows the filter to fill no more than 110 // output_buffer_length in any one call to proccess (a.k.a., Read) data, and 111 // concatenates all these little output blocks into the singular output string. 112 static bool FilterTestData(const std::string& source, 113 size_t input_block_length, 114 const size_t output_buffer_length, 115 Filter* filter, std::string* output) { 116 CHECK_GT(input_block_length, 0u); 117 Filter::FilterStatus status(Filter::FILTER_NEED_MORE_DATA); 118 size_t source_index = 0; 119 scoped_array<char> output_buffer(new char[output_buffer_length]); 120 size_t input_amount = std::min(input_block_length, 121 static_cast<size_t>(filter->stream_buffer_size())); 122 123 do { 124 int copy_amount = std::min(input_amount, source.size() - source_index); 125 if (copy_amount > 0 && status == Filter::FILTER_NEED_MORE_DATA) { 126 memcpy(filter->stream_buffer()->data(), source.data() + source_index, 127 copy_amount); 128 filter->FlushStreamBuffer(copy_amount); 129 source_index += copy_amount; 130 } 131 int buffer_length = output_buffer_length; 132 status = filter->ReadData(output_buffer.get(), &buffer_length); 133 output->append(output_buffer.get(), buffer_length); 134 if (status == Filter::FILTER_ERROR) 135 return false; 136 // Callers assume that FILTER_OK with no output buffer means FILTER_DONE. 137 if (Filter::FILTER_OK == status && 0 == buffer_length) 138 return true; 139 if (copy_amount == 0 && buffer_length == 0) 140 return true; 141 } while (1); 142 } 143 //------------------------------------------------------------------------------ 144 static std::string NewSdchDictionary(const std::string& domain) { 145 std::string dictionary; 146 if (!domain.empty()) { 147 dictionary.append("Domain: "); 148 dictionary.append(domain); 149 dictionary.append("\n"); 150 } 151 dictionary.append("\n"); 152 dictionary.append(kTestVcdiffDictionary, sizeof(kTestVcdiffDictionary) - 1); 153 return dictionary; 154 } 155 156 //------------------------------------------------------------------------------ 157 158 TEST_F(SdchFilterTest, EmptyInputOk) { 159 std::vector<Filter::FilterType> filter_types; 160 filter_types.push_back(Filter::FILTER_TYPE_SDCH); 161 char output_buffer[20]; 162 MockFilterContext filter_context; 163 std::string url_string("http://ignore.com"); 164 filter_context.SetURL(GURL(url_string)); 165 scoped_ptr<Filter> filter(Filter::Factory(filter_types, filter_context)); 166 167 168 // With no input data, try to read output. 169 int output_bytes_or_buffer_size = sizeof(output_buffer); 170 Filter::FilterStatus status = filter->ReadData(output_buffer, 171 &output_bytes_or_buffer_size); 172 173 EXPECT_EQ(0, output_bytes_or_buffer_size); 174 EXPECT_EQ(Filter::FILTER_NEED_MORE_DATA, status); 175 } 176 177 TEST_F(SdchFilterTest, PassThroughWhenTentative) { 178 std::vector<Filter::FilterType> filter_types; 179 // Selective a tentative filter (which can fall back to pass through). 180 filter_types.push_back(Filter::FILTER_TYPE_GZIP_HELPING_SDCH); 181 char output_buffer[20]; 182 MockFilterContext filter_context; 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 MockFilterContext filter_context; 221 // Response code needs to be 200 to allow a pass through. 222 filter_context.SetResponseCode(403); 223 // Meta refresh will only appear for html content 224 filter_context.SetMimeType("text/html"); 225 std::string url_string("http://ignore.com"); 226 filter_context.SetURL(GURL(url_string)); 227 scoped_ptr<Filter> filter(Filter::Factory(filter_types, filter_context)); 228 229 // Supply enough data to force a pass-through mode, which means we have 230 // provided more than 9 characters that can't be a dictionary hash. 231 std::string non_sdch_content("This is not SDCH"); 232 233 char* input_buffer = filter->stream_buffer()->data(); 234 int input_buffer_size = filter->stream_buffer_size(); 235 236 EXPECT_LT(static_cast<int>(non_sdch_content.size()), 237 input_buffer_size); 238 memcpy(input_buffer, non_sdch_content.data(), 239 non_sdch_content.size()); 240 filter->FlushStreamBuffer(non_sdch_content.size()); 241 242 // Try to read output. 243 int output_bytes_or_buffer_size = sizeof(output_buffer); 244 Filter::FilterStatus status = filter->ReadData(output_buffer, 245 &output_bytes_or_buffer_size); 246 247 // We should have read a long and complicated meta-refresh request. 248 EXPECT_TRUE(sizeof(output_buffer) == output_bytes_or_buffer_size); 249 // Check at least the prefix of the return. 250 EXPECT_EQ(0, strncmp(output_buffer, 251 "<head><META HTTP-EQUIV=\"Refresh\" CONTENT=\"0\"></head>", 252 sizeof(output_buffer))); 253 EXPECT_EQ(Filter::FILTER_OK, status); 254 } 255 256 TEST_F(SdchFilterTest, ErrorOnBadReturnCode) { 257 std::vector<Filter::FilterType> filter_types; 258 // Selective a tentative filter (which can fall back to pass through). 259 filter_types.push_back(Filter::FILTER_TYPE_SDCH_POSSIBLE); 260 char output_buffer[20]; 261 MockFilterContext filter_context; 262 // Response code needs to be 200 to allow a pass through. 263 filter_context.SetResponseCode(403); 264 // Meta refresh will only appear for html content, so set to something else 265 // to induce an error (we can't meta refresh). 266 filter_context.SetMimeType("anything"); 267 std::string url_string("http://ignore.com"); 268 filter_context.SetURL(GURL(url_string)); 269 scoped_ptr<Filter> filter(Filter::Factory(filter_types, filter_context)); 270 271 // Supply enough data to force a pass-through mode, which means we have 272 // provided more than 9 characters that can't be a dictionary hash. 273 std::string non_sdch_content("This is not SDCH"); 274 275 char* input_buffer = filter->stream_buffer()->data(); 276 int input_buffer_size = filter->stream_buffer_size(); 277 278 EXPECT_LT(static_cast<int>(non_sdch_content.size()), 279 input_buffer_size); 280 memcpy(input_buffer, non_sdch_content.data(), 281 non_sdch_content.size()); 282 filter->FlushStreamBuffer(non_sdch_content.size()); 283 284 // Try to read output. 285 int output_bytes_or_buffer_size = sizeof(output_buffer); 286 Filter::FilterStatus status = filter->ReadData(output_buffer, 287 &output_bytes_or_buffer_size); 288 289 EXPECT_EQ(0, output_bytes_or_buffer_size); 290 EXPECT_EQ(Filter::FILTER_ERROR, status); 291 } 292 293 TEST_F(SdchFilterTest, ErrorOnBadReturnCodeWithHtml) { 294 std::vector<Filter::FilterType> filter_types; 295 // Selective a tentative filter (which can fall back to pass through). 296 filter_types.push_back(Filter::FILTER_TYPE_SDCH_POSSIBLE); 297 char output_buffer[20]; 298 MockFilterContext filter_context; 299 // Response code needs to be 200 to allow a pass through. 300 filter_context.SetResponseCode(403); 301 // Meta refresh will only appear for html content 302 filter_context.SetMimeType("text/html"); 303 std::string url_string("http://ignore.com"); 304 filter_context.SetURL(GURL(url_string)); 305 scoped_ptr<Filter> filter(Filter::Factory(filter_types, filter_context)); 306 307 // Supply enough data to force a pass-through mode, which means we have 308 // provided more than 9 characters that can't be a dictionary hash. 309 std::string non_sdch_content("This is not SDCH"); 310 311 char* input_buffer = filter->stream_buffer()->data(); 312 int input_buffer_size = filter->stream_buffer_size(); 313 314 EXPECT_LT(static_cast<int>(non_sdch_content.size()), 315 input_buffer_size); 316 memcpy(input_buffer, non_sdch_content.data(), 317 non_sdch_content.size()); 318 filter->FlushStreamBuffer(non_sdch_content.size()); 319 320 // Try to read output. 321 int output_bytes_or_buffer_size = sizeof(output_buffer); 322 Filter::FilterStatus status = filter->ReadData(output_buffer, 323 &output_bytes_or_buffer_size); 324 325 // We should have read a long and complicated meta-refresh request. 326 EXPECT_EQ(sizeof(output_buffer), 327 static_cast<size_t>(output_bytes_or_buffer_size)); 328 // Check at least the prefix of the return. 329 EXPECT_EQ(0, strncmp(output_buffer, 330 "<head><META HTTP-EQUIV=\"Refresh\" CONTENT=\"0\"></head>", 331 sizeof(output_buffer))); 332 EXPECT_EQ(Filter::FILTER_OK, status); 333 } 334 335 TEST_F(SdchFilterTest, BasicBadDictionary) { 336 std::vector<Filter::FilterType> filter_types; 337 filter_types.push_back(Filter::FILTER_TYPE_SDCH); 338 char output_buffer[20]; 339 MockFilterContext filter_context; 340 std::string url_string("http://ignore.com"); 341 filter_context.SetURL(GURL(url_string)); 342 scoped_ptr<Filter> filter(Filter::Factory(filter_types, filter_context)); 343 344 // Supply bogus data (which doesn't yet specify a full dictionary hash). 345 // Dictionary hash is 8 characters followed by a null. 346 std::string dictionary_hash_prefix("123"); 347 348 char* input_buffer = filter->stream_buffer()->data(); 349 int input_buffer_size = filter->stream_buffer_size(); 350 351 EXPECT_LT(static_cast<int>(dictionary_hash_prefix.size()), 352 input_buffer_size); 353 memcpy(input_buffer, dictionary_hash_prefix.data(), 354 dictionary_hash_prefix.size()); 355 filter->FlushStreamBuffer(dictionary_hash_prefix.size()); 356 357 // With less than a dictionary specifier, try to read output. 358 int output_bytes_or_buffer_size = sizeof(output_buffer); 359 Filter::FilterStatus status = filter->ReadData(output_buffer, 360 &output_bytes_or_buffer_size); 361 362 EXPECT_EQ(0, output_bytes_or_buffer_size); 363 EXPECT_EQ(Filter::FILTER_NEED_MORE_DATA, status); 364 365 // Provide enough data to complete *a* hash, but it is bogus, and not in our 366 // list of dictionaries, so the filter should error out immediately. 367 std::string dictionary_hash_postfix("4abcd\0", 6); 368 369 CHECK_LT(dictionary_hash_postfix.size(), 370 static_cast<size_t>(input_buffer_size)); 371 memcpy(input_buffer, dictionary_hash_postfix.data(), 372 dictionary_hash_postfix.size()); 373 filter->FlushStreamBuffer(dictionary_hash_postfix.size()); 374 375 // With a non-existant dictionary specifier, try to read output. 376 output_bytes_or_buffer_size = sizeof(output_buffer); 377 status = filter->ReadData(output_buffer, &output_bytes_or_buffer_size); 378 379 EXPECT_EQ(0, output_bytes_or_buffer_size); 380 EXPECT_EQ(Filter::FILTER_ERROR, status); 381 382 EXPECT_FALSE(SdchManager::Global()->IsInSupportedDomain(GURL(url_string))); 383 SdchManager::ClearBlacklistings(); 384 EXPECT_TRUE(SdchManager::Global()->IsInSupportedDomain(GURL(url_string))); 385 } 386 387 TEST_F(SdchFilterTest, DictionaryAddOnce) { 388 // Construct a valid SDCH dictionary from a VCDIFF dictionary. 389 const std::string kSampleDomain = "sdchtest.com"; 390 std::string dictionary(NewSdchDictionary(kSampleDomain)); 391 392 std::string url_string = "http://" + kSampleDomain; 393 GURL url(url_string); 394 EXPECT_TRUE(sdch_manager_->AddSdchDictionary(dictionary, url)); 395 396 // Check we can't add it twice. 397 EXPECT_FALSE(sdch_manager_->AddSdchDictionary(dictionary, url)); 398 399 const std::string kSampleDomain2 = "sdchtest2.com"; 400 401 // Construct a second SDCH dictionary from a VCDIFF dictionary. 402 std::string dictionary2(NewSdchDictionary(kSampleDomain2)); 403 404 std::string url_string2 = "http://" + kSampleDomain2; 405 GURL url2(url_string2); 406 EXPECT_TRUE(sdch_manager_->AddSdchDictionary(dictionary2, url2)); 407 } 408 409 TEST_F(SdchFilterTest, BasicDictionary) { 410 // Construct a valid SDCH dictionary from a VCDIFF dictionary. 411 const std::string kSampleDomain = "sdchtest.com"; 412 std::string dictionary(NewSdchDictionary(kSampleDomain)); 413 414 std::string url_string = "http://" + kSampleDomain; 415 416 GURL url(url_string); 417 EXPECT_TRUE(sdch_manager_->AddSdchDictionary(dictionary, url)); 418 419 std::string compressed(NewSdchCompressedData(dictionary)); 420 421 std::vector<Filter::FilterType> filter_types; 422 filter_types.push_back(Filter::FILTER_TYPE_SDCH); 423 424 MockFilterContext filter_context; 425 filter_context.SetURL(url); 426 427 scoped_ptr<Filter> filter(Filter::Factory(filter_types, filter_context)); 428 429 size_t feed_block_size = 100; 430 size_t output_block_size = 100; 431 std::string output; 432 EXPECT_TRUE(FilterTestData(compressed, feed_block_size, output_block_size, 433 filter.get(), &output)); 434 EXPECT_EQ(output, expanded_); 435 436 // Decode with really small buffers (size 1) to check for edge effects. 437 filter.reset(Filter::Factory(filter_types, filter_context)); 438 439 feed_block_size = 1; 440 output_block_size = 1; 441 output.clear(); 442 EXPECT_TRUE(FilterTestData(compressed, feed_block_size, output_block_size, 443 filter.get(), &output)); 444 EXPECT_EQ(output, expanded_); 445 } 446 447 TEST_F(SdchFilterTest, NoDecodeHttps) { 448 // Construct a valid SDCH dictionary from a VCDIFF dictionary. 449 const std::string kSampleDomain = "sdchtest.com"; 450 std::string dictionary(NewSdchDictionary(kSampleDomain)); 451 452 std::string url_string = "http://" + kSampleDomain; 453 454 GURL url(url_string); 455 EXPECT_TRUE(sdch_manager_->AddSdchDictionary(dictionary, url)); 456 457 std::string compressed(NewSdchCompressedData(dictionary)); 458 459 std::vector<Filter::FilterType> filter_types; 460 filter_types.push_back(Filter::FILTER_TYPE_SDCH); 461 462 MockFilterContext filter_context; 463 filter_context.SetURL(GURL("https://" + kSampleDomain)); 464 scoped_ptr<Filter> filter(Filter::Factory(filter_types, filter_context)); 465 466 const size_t feed_block_size(100); 467 const size_t output_block_size(100); 468 std::string output; 469 470 EXPECT_FALSE(FilterTestData(compressed, feed_block_size, output_block_size, 471 filter.get(), &output)); 472 } 473 474 // Current failsafe TODO/hack refuses to decode any content that doesn't use 475 // http as the scheme (see use of DICTIONARY_SELECTED_FOR_NON_HTTP). 476 // The following tests this blockage. Note that blacklisting results, so we 477 // we need separate tests for each of these. 478 TEST_F(SdchFilterTest, NoDecodeFtp) { 479 // Construct a valid SDCH dictionary from a VCDIFF dictionary. 480 const std::string kSampleDomain = "sdchtest.com"; 481 std::string dictionary(NewSdchDictionary(kSampleDomain)); 482 483 std::string url_string = "http://" + kSampleDomain; 484 485 GURL url(url_string); 486 EXPECT_TRUE(sdch_manager_->AddSdchDictionary(dictionary, url)); 487 488 std::string compressed(NewSdchCompressedData(dictionary)); 489 490 std::vector<Filter::FilterType> filter_types; 491 filter_types.push_back(Filter::FILTER_TYPE_SDCH); 492 493 MockFilterContext filter_context; 494 filter_context.SetURL(GURL("ftp://" + kSampleDomain)); 495 scoped_ptr<Filter> filter(Filter::Factory(filter_types, filter_context)); 496 497 const size_t feed_block_size(100); 498 const size_t output_block_size(100); 499 std::string output; 500 501 EXPECT_FALSE(FilterTestData(compressed, feed_block_size, output_block_size, 502 filter.get(), &output)); 503 } 504 505 TEST_F(SdchFilterTest, NoDecodeFileColon) { 506 // Construct a valid SDCH dictionary from a VCDIFF dictionary. 507 const std::string kSampleDomain = "sdchtest.com"; 508 std::string dictionary(NewSdchDictionary(kSampleDomain)); 509 510 std::string url_string = "http://" + kSampleDomain; 511 512 GURL url(url_string); 513 EXPECT_TRUE(sdch_manager_->AddSdchDictionary(dictionary, url)); 514 515 std::string compressed(NewSdchCompressedData(dictionary)); 516 517 std::vector<Filter::FilterType> filter_types; 518 filter_types.push_back(Filter::FILTER_TYPE_SDCH); 519 520 MockFilterContext filter_context; 521 filter_context.SetURL(GURL("file://" + kSampleDomain)); 522 scoped_ptr<Filter> filter(Filter::Factory(filter_types, filter_context)); 523 524 const size_t feed_block_size(100); 525 const size_t output_block_size(100); 526 std::string output; 527 528 EXPECT_FALSE(FilterTestData(compressed, feed_block_size, output_block_size, 529 filter.get(), &output)); 530 } 531 532 TEST_F(SdchFilterTest, NoDecodeAboutColon) { 533 // Construct a valid SDCH dictionary from a VCDIFF dictionary. 534 const std::string kSampleDomain = "sdchtest.com"; 535 std::string dictionary(NewSdchDictionary(kSampleDomain)); 536 537 std::string url_string = "http://" + kSampleDomain; 538 539 GURL url(url_string); 540 EXPECT_TRUE(sdch_manager_->AddSdchDictionary(dictionary, url)); 541 542 std::string compressed(NewSdchCompressedData(dictionary)); 543 544 std::vector<Filter::FilterType> filter_types; 545 filter_types.push_back(Filter::FILTER_TYPE_SDCH); 546 547 MockFilterContext filter_context; 548 filter_context.SetURL(GURL("about://" + kSampleDomain)); 549 scoped_ptr<Filter> filter(Filter::Factory(filter_types, filter_context)); 550 551 const size_t feed_block_size(100); 552 const size_t output_block_size(100); 553 std::string output; 554 555 EXPECT_FALSE(FilterTestData(compressed, feed_block_size, output_block_size, 556 filter.get(), &output)); 557 } 558 559 TEST_F(SdchFilterTest, NoDecodeJavaScript) { 560 // Construct a valid SDCH dictionary from a VCDIFF dictionary. 561 const std::string kSampleDomain = "sdchtest.com"; 562 std::string dictionary(NewSdchDictionary(kSampleDomain)); 563 564 std::string url_string = "http://" + kSampleDomain; 565 566 GURL url(url_string); 567 EXPECT_TRUE(sdch_manager_->AddSdchDictionary(dictionary, url)); 568 569 std::string compressed(NewSdchCompressedData(dictionary)); 570 571 std::vector<Filter::FilterType> filter_types; 572 filter_types.push_back(Filter::FILTER_TYPE_SDCH); 573 574 MockFilterContext filter_context; 575 filter_context.SetURL(GURL("javascript://" + kSampleDomain)); 576 scoped_ptr<Filter> filter(Filter::Factory(filter_types, filter_context)); 577 578 const size_t feed_block_size(100); 579 const size_t output_block_size(100); 580 std::string output; 581 582 EXPECT_FALSE(FilterTestData(compressed, feed_block_size, output_block_size, 583 filter.get(), &output)); 584 } 585 586 TEST_F(SdchFilterTest, CanStillDecodeHttp) { 587 // Construct a valid SDCH dictionary from a VCDIFF dictionary. 588 const std::string kSampleDomain = "sdchtest.com"; 589 std::string dictionary(NewSdchDictionary(kSampleDomain)); 590 591 std::string url_string = "http://" + kSampleDomain; 592 593 GURL url(url_string); 594 EXPECT_TRUE(sdch_manager_->AddSdchDictionary(dictionary, url)); 595 596 std::string compressed(NewSdchCompressedData(dictionary)); 597 598 std::vector<Filter::FilterType> filter_types; 599 filter_types.push_back(Filter::FILTER_TYPE_SDCH); 600 601 MockFilterContext filter_context; 602 filter_context.SetURL(GURL("http://" + kSampleDomain)); 603 scoped_ptr<Filter> filter(Filter::Factory(filter_types, filter_context)); 604 605 const size_t feed_block_size(100); 606 const size_t output_block_size(100); 607 std::string output; 608 609 EXPECT_TRUE(FilterTestData(compressed, feed_block_size, output_block_size, 610 filter.get(), &output)); 611 } 612 613 TEST_F(SdchFilterTest, CrossDomainDictionaryUse) { 614 // Construct a valid SDCH dictionary from a VCDIFF dictionary. 615 const std::string kSampleDomain = "sdchtest.com"; 616 std::string dictionary(NewSdchDictionary(kSampleDomain)); 617 618 std::string url_string = "http://" + kSampleDomain; 619 620 GURL url(url_string); 621 EXPECT_TRUE(sdch_manager_->AddSdchDictionary(dictionary, url)); 622 623 std::string compressed(NewSdchCompressedData(dictionary)); 624 625 std::vector<Filter::FilterType> filter_types; 626 filter_types.push_back(Filter::FILTER_TYPE_SDCH); 627 628 // Decode with content arriving from the "wrong" domain. 629 // This tests SdchManager::CanSet(). 630 MockFilterContext filter_context; 631 GURL wrong_domain_url("http://www.wrongdomain.com"); 632 filter_context.SetURL(wrong_domain_url); 633 scoped_ptr<Filter> filter(Filter::Factory(filter_types, filter_context)); 634 635 size_t feed_block_size = 100; 636 size_t output_block_size = 100; 637 std::string output; 638 EXPECT_FALSE(FilterTestData(compressed, feed_block_size, output_block_size, 639 filter.get(), &output)); 640 EXPECT_EQ(output.size(), 0u); // No output written. 641 642 EXPECT_TRUE(SdchManager::Global()->IsInSupportedDomain(GURL(url_string))); 643 EXPECT_FALSE(SdchManager::Global()->IsInSupportedDomain(wrong_domain_url)); 644 SdchManager::ClearBlacklistings(); 645 EXPECT_TRUE(SdchManager::Global()->IsInSupportedDomain(wrong_domain_url)); 646 } 647 648 TEST_F(SdchFilterTest, DictionaryPathValidation) { 649 // Construct a valid SDCH dictionary from a VCDIFF dictionary. 650 const std::string kSampleDomain = "sdchtest.com"; 651 std::string dictionary(NewSdchDictionary(kSampleDomain)); 652 653 std::string url_string = "http://" + kSampleDomain; 654 655 GURL url(url_string); 656 EXPECT_TRUE(sdch_manager_->AddSdchDictionary(dictionary, url)); 657 658 // Create a dictionary with a path restriction, by prefixing dictionary. 659 const std::string path("/special_path/bin"); 660 std::string dictionary_with_path("Path: " + path + "\n"); 661 dictionary_with_path.append(dictionary); 662 EXPECT_TRUE(sdch_manager_->AddSdchDictionary(dictionary_with_path, url)); 663 664 std::string compressed_for_path(NewSdchCompressedData(dictionary_with_path)); 665 666 std::vector<Filter::FilterType> filter_types; 667 filter_types.push_back(Filter::FILTER_TYPE_SDCH); 668 669 // Test decode the path data, arriving from a valid path. 670 MockFilterContext filter_context; 671 filter_context.SetURL(GURL(url_string + path)); 672 scoped_ptr<Filter> filter(Filter::Factory(filter_types, filter_context)); 673 674 size_t feed_block_size = 100; 675 size_t output_block_size = 100; 676 std::string output; 677 678 EXPECT_TRUE(FilterTestData(compressed_for_path, feed_block_size, 679 output_block_size, filter.get(), &output)); 680 EXPECT_EQ(output, expanded_); 681 682 // Test decode the path data, arriving from a invalid path. 683 filter_context.SetURL(GURL(url_string)); 684 filter.reset(Filter::Factory(filter_types, filter_context)); 685 686 feed_block_size = 100; 687 output_block_size = 100; 688 output.clear(); 689 EXPECT_FALSE(FilterTestData(compressed_for_path, feed_block_size, 690 output_block_size, filter.get(), &output)); 691 EXPECT_EQ(output.size(), 0u); // No output written. 692 693 EXPECT_FALSE(SdchManager::Global()->IsInSupportedDomain(GURL(url_string))); 694 SdchManager::ClearBlacklistings(); 695 EXPECT_TRUE(SdchManager::Global()->IsInSupportedDomain(GURL(url_string))); 696 } 697 698 TEST_F(SdchFilterTest, DictionaryPortValidation) { 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 MockFilterContext filter_context; 724 filter_context.SetURL(GURL(url_string + ":" + port)); 725 scoped_ptr<Filter> filter(Filter::Factory(filter_types, filter_context)); 726 727 size_t feed_block_size = 100; 728 size_t output_block_size = 100; 729 std::string output; 730 EXPECT_TRUE(FilterTestData(compressed_for_port, feed_block_size, 731 output_block_size, filter.get(), &output)); 732 EXPECT_EQ(output, expanded_); 733 734 // Test decode the port data, arriving from a valid (default) port. 735 filter_context.SetURL(GURL(url_string)); // Default port. 736 filter.reset(Filter::Factory(filter_types, filter_context)); 737 738 feed_block_size = 100; 739 output_block_size = 100; 740 output.clear(); 741 EXPECT_TRUE(FilterTestData(compressed_for_port, feed_block_size, 742 output_block_size, filter.get(), &output)); 743 EXPECT_EQ(output, expanded_); 744 745 // Test decode the port data, arriving from a invalid port. 746 filter_context.SetURL(GURL(url_string + ":" + port + "1")); 747 filter.reset(Filter::Factory(filter_types, filter_context)); 748 749 feed_block_size = 100; 750 output_block_size = 100; 751 output.clear(); 752 EXPECT_FALSE(FilterTestData(compressed_for_port, feed_block_size, 753 output_block_size, filter.get(), &output)); 754 EXPECT_EQ(output.size(), 0u); // No output written. 755 756 EXPECT_FALSE(SdchManager::Global()->IsInSupportedDomain(GURL(url_string))); 757 SdchManager::ClearBlacklistings(); 758 EXPECT_TRUE(SdchManager::Global()->IsInSupportedDomain(GURL(url_string))); 759 } 760 761 //------------------------------------------------------------------------------ 762 // Helper function to perform gzip compression of data. 763 764 static std::string gzip_compress(const std::string &input) { 765 z_stream zlib_stream; 766 memset(&zlib_stream, 0, sizeof(zlib_stream)); 767 int code; 768 769 // Initialize zlib 770 code = deflateInit2(&zlib_stream, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 771 -MAX_WBITS, 772 8, // DEF_MEM_LEVEL 773 Z_DEFAULT_STRATEGY); 774 775 CHECK_EQ(Z_OK, code); 776 777 // Fill in zlib control block 778 zlib_stream.next_in = bit_cast<Bytef*>(input.data()); 779 zlib_stream.avail_in = input.size(); 780 781 // Assume we can compress into similar buffer (add 100 bytes to be sure). 782 size_t gzip_compressed_length = zlib_stream.avail_in + 100; 783 scoped_array<char> gzip_compressed(new char[gzip_compressed_length]); 784 zlib_stream.next_out = bit_cast<Bytef*>(gzip_compressed.get()); 785 zlib_stream.avail_out = gzip_compressed_length; 786 787 // The GZIP header (see RFC 1952): 788 // +---+---+---+---+---+---+---+---+---+---+ 789 // |ID1|ID2|CM |FLG| MTIME |XFL|OS | 790 // +---+---+---+---+---+---+---+---+---+---+ 791 // ID1 \037 792 // ID2 \213 793 // CM \010 (compression method == DEFLATE) 794 // FLG \000 (special flags that we do not support) 795 // MTIME Unix format modification time (0 means not available) 796 // XFL 2-4? DEFLATE flags 797 // OS ???? Operating system indicator (255 means unknown) 798 // 799 // Header value we generate: 800 const char kGZipHeader[] = { '\037', '\213', '\010', '\000', '\000', 801 '\000', '\000', '\000', '\002', '\377' }; 802 CHECK_GT(zlib_stream.avail_out, sizeof(kGZipHeader)); 803 memcpy(zlib_stream.next_out, kGZipHeader, sizeof(kGZipHeader)); 804 zlib_stream.next_out += sizeof(kGZipHeader); 805 zlib_stream.avail_out -= sizeof(kGZipHeader); 806 807 // Do deflate 808 code = deflate(&zlib_stream, Z_FINISH); 809 gzip_compressed_length -= zlib_stream.avail_out; 810 std::string compressed(gzip_compressed.get(), gzip_compressed_length); 811 deflateEnd(&zlib_stream); 812 return compressed; 813 } 814 815 //------------------------------------------------------------------------------ 816 817 class SdchFilterChainingTest { 818 public: 819 static Filter* Factory(const std::vector<Filter::FilterType>& types, 820 const FilterContext& context, int size) { 821 return Filter::FactoryForTests(types, context, size); 822 } 823 }; 824 825 // Test that filters can be cascaded (chained) so that the output of one filter 826 // is processed by the next one. This is most critical for SDCH, which is 827 // routinely followed by gzip (during encoding). The filter we'll test for will 828 // do the gzip decoding first, and then decode the SDCH content. 829 TEST_F(SdchFilterTest, FilterChaining) { 830 // Construct a valid SDCH dictionary from a VCDIFF dictionary. 831 const std::string kSampleDomain = "sdchtest.com"; 832 std::string dictionary(NewSdchDictionary(kSampleDomain)); 833 834 std::string url_string = "http://" + kSampleDomain; 835 836 GURL url(url_string); 837 EXPECT_TRUE(sdch_manager_->AddSdchDictionary(dictionary, url)); 838 839 std::string sdch_compressed(NewSdchCompressedData(dictionary)); 840 841 // Use Gzip to compress the sdch sdch_compressed data. 842 std::string gzip_compressed_sdch = gzip_compress(sdch_compressed); 843 844 // Construct a chained filter. 845 std::vector<Filter::FilterType> filter_types; 846 filter_types.push_back(Filter::FILTER_TYPE_SDCH); 847 filter_types.push_back(Filter::FILTER_TYPE_GZIP); 848 849 // First try with a large buffer (larger than test input, or compressed data). 850 const size_t kLargeInputBufferSize(1000); // Used internally in filters. 851 CHECK_GT(kLargeInputBufferSize, gzip_compressed_sdch.size()); 852 CHECK_GT(kLargeInputBufferSize, sdch_compressed.size()); 853 CHECK_GT(kLargeInputBufferSize, expanded_.size()); 854 MockFilterContext filter_context; 855 filter_context.SetURL(url); 856 scoped_ptr<Filter> filter( 857 SdchFilterChainingTest::Factory(filter_types, filter_context, 858 kLargeInputBufferSize)); 859 EXPECT_EQ(static_cast<int>(kLargeInputBufferSize), 860 filter->stream_buffer_size()); 861 862 // Verify that chained filter is waiting for data. 863 char tiny_output_buffer[10]; 864 int tiny_output_size = sizeof(tiny_output_buffer); 865 EXPECT_EQ(Filter::FILTER_NEED_MORE_DATA, 866 filter->ReadData(tiny_output_buffer, &tiny_output_size)); 867 868 // Make chain process all data. 869 size_t feed_block_size = kLargeInputBufferSize; 870 size_t output_block_size = kLargeInputBufferSize; 871 std::string output; 872 EXPECT_TRUE(FilterTestData(gzip_compressed_sdch, feed_block_size, 873 output_block_size, filter.get(), &output)); 874 EXPECT_EQ(output, expanded_); 875 876 // Next try with a mid-sized internal buffer size. 877 const size_t kMidSizedInputBufferSize(100); 878 // Buffer should be big enough to swallow whole gzip content. 879 CHECK_GT(kMidSizedInputBufferSize, gzip_compressed_sdch.size()); 880 // Buffer should be small enough that entire SDCH content can't fit. 881 // We'll go even further, and force the chain to flush the buffer between the 882 // two filters more than once (that is why we multiply by 2). 883 CHECK_LT(kMidSizedInputBufferSize * 2, sdch_compressed.size()); 884 filter_context.SetURL(url); 885 filter.reset( 886 SdchFilterChainingTest::Factory(filter_types, filter_context, 887 kMidSizedInputBufferSize)); 888 EXPECT_EQ(static_cast<int>(kMidSizedInputBufferSize), 889 filter->stream_buffer_size()); 890 891 feed_block_size = kMidSizedInputBufferSize; 892 output_block_size = kMidSizedInputBufferSize; 893 output.clear(); 894 EXPECT_TRUE(FilterTestData(gzip_compressed_sdch, feed_block_size, 895 output_block_size, filter.get(), &output)); 896 EXPECT_EQ(output, expanded_); 897 898 // Next try with a tiny input and output buffer to cover edge effects. 899 filter.reset(SdchFilterChainingTest::Factory(filter_types, filter_context, 900 kLargeInputBufferSize)); 901 EXPECT_EQ(static_cast<int>(kLargeInputBufferSize), 902 filter->stream_buffer_size()); 903 904 feed_block_size = 1; 905 output_block_size = 1; 906 output.clear(); 907 EXPECT_TRUE(FilterTestData(gzip_compressed_sdch, feed_block_size, 908 output_block_size, filter.get(), &output)); 909 EXPECT_EQ(output, expanded_); 910 } 911 912 TEST_F(SdchFilterTest, DefaultGzipIfSdch) { 913 // Construct a valid SDCH dictionary from a VCDIFF dictionary. 914 const std::string kSampleDomain = "sdchtest.com"; 915 std::string dictionary(NewSdchDictionary(kSampleDomain)); 916 917 std::string url_string = "http://" + kSampleDomain; 918 919 GURL url(url_string); 920 EXPECT_TRUE(sdch_manager_->AddSdchDictionary(dictionary, url)); 921 922 std::string sdch_compressed(NewSdchCompressedData(dictionary)); 923 924 // Use Gzip to compress the sdch sdch_compressed data. 925 std::string gzip_compressed_sdch = gzip_compress(sdch_compressed); 926 927 // Only claim to have sdch content, but really use the gzipped sdch content. 928 // System should automatically add the missing (optional) gzip. 929 std::vector<Filter::FilterType> filter_types; 930 filter_types.push_back(Filter::FILTER_TYPE_SDCH); 931 932 MockFilterContext filter_context; 933 filter_context.SetMimeType("anything/mime"); 934 filter_context.SetSdchResponse(true); 935 Filter::FixupEncodingTypes(filter_context, &filter_types); 936 ASSERT_EQ(filter_types.size(), 2u); 937 EXPECT_EQ(filter_types[0], Filter::FILTER_TYPE_SDCH); 938 EXPECT_EQ(filter_types[1], Filter::FILTER_TYPE_GZIP_HELPING_SDCH); 939 940 // First try with a large buffer (larger than test input, or compressed data). 941 filter_context.SetURL(url); 942 scoped_ptr<Filter> filter(Filter::Factory(filter_types, filter_context)); 943 944 945 // Verify that chained filter is waiting for data. 946 char tiny_output_buffer[10]; 947 int tiny_output_size = sizeof(tiny_output_buffer); 948 EXPECT_EQ(Filter::FILTER_NEED_MORE_DATA, 949 filter->ReadData(tiny_output_buffer, &tiny_output_size)); 950 951 size_t feed_block_size = 100; 952 size_t output_block_size = 100; 953 std::string output; 954 EXPECT_TRUE(FilterTestData(gzip_compressed_sdch, feed_block_size, 955 output_block_size, filter.get(), &output)); 956 EXPECT_EQ(output, expanded_); 957 958 // Next try with a tiny buffer to cover edge effects. 959 filter.reset(Filter::Factory(filter_types, filter_context)); 960 961 feed_block_size = 1; 962 output_block_size = 1; 963 output.clear(); 964 EXPECT_TRUE(FilterTestData(gzip_compressed_sdch, feed_block_size, 965 output_block_size, filter.get(), &output)); 966 EXPECT_EQ(output, expanded_); 967 } 968 969 TEST_F(SdchFilterTest, AcceptGzipSdchIfGzip) { 970 // Construct a valid SDCH dictionary from a VCDIFF dictionary. 971 const std::string kSampleDomain = "sdchtest.com"; 972 std::string dictionary(NewSdchDictionary(kSampleDomain)); 973 974 std::string url_string = "http://" + kSampleDomain; 975 976 GURL url(url_string); 977 EXPECT_TRUE(sdch_manager_->AddSdchDictionary(dictionary, url)); 978 979 std::string sdch_compressed(NewSdchCompressedData(dictionary)); 980 981 // Use Gzip to compress the sdch sdch_compressed data. 982 std::string gzip_compressed_sdch = gzip_compress(sdch_compressed); 983 984 // Some proxies strip the content encoding statement down to a mere gzip, but 985 // pass through the original content (with full sdch,gzip encoding). 986 // Only claim to have gzip content, but really use the gzipped sdch content. 987 // System should automatically add the missing (optional) sdch. 988 std::vector<Filter::FilterType> filter_types; 989 filter_types.push_back(Filter::FILTER_TYPE_GZIP); 990 991 MockFilterContext filter_context; 992 filter_context.SetMimeType("anything/mime"); 993 filter_context.SetSdchResponse(true); 994 Filter::FixupEncodingTypes(filter_context, &filter_types); 995 ASSERT_EQ(filter_types.size(), 3u); 996 EXPECT_EQ(filter_types[0], Filter::FILTER_TYPE_SDCH_POSSIBLE); 997 EXPECT_EQ(filter_types[1], Filter::FILTER_TYPE_GZIP_HELPING_SDCH); 998 EXPECT_EQ(filter_types[2], Filter::FILTER_TYPE_GZIP); 999 1000 // First try with a large buffer (larger than test input, or compressed data). 1001 filter_context.SetURL(url); 1002 scoped_ptr<Filter> filter(Filter::Factory(filter_types, filter_context)); 1003 1004 1005 // Verify that chained filter is waiting for data. 1006 char tiny_output_buffer[10]; 1007 int tiny_output_size = sizeof(tiny_output_buffer); 1008 EXPECT_EQ(Filter::FILTER_NEED_MORE_DATA, 1009 filter->ReadData(tiny_output_buffer, &tiny_output_size)); 1010 1011 size_t feed_block_size = 100; 1012 size_t output_block_size = 100; 1013 std::string output; 1014 EXPECT_TRUE(FilterTestData(gzip_compressed_sdch, feed_block_size, 1015 output_block_size, filter.get(), &output)); 1016 EXPECT_EQ(output, expanded_); 1017 1018 // Next try with a tiny buffer to cover edge effects. 1019 filter.reset(Filter::Factory(filter_types, filter_context)); 1020 1021 feed_block_size = 1; 1022 output_block_size = 1; 1023 output.clear(); 1024 EXPECT_TRUE(FilterTestData(gzip_compressed_sdch, feed_block_size, 1025 output_block_size, filter.get(), &output)); 1026 EXPECT_EQ(output, expanded_); 1027 } 1028 1029 TEST_F(SdchFilterTest, DefaultSdchGzipIfEmpty) { 1030 // Construct a valid SDCH dictionary from a VCDIFF dictionary. 1031 const std::string kSampleDomain = "sdchtest.com"; 1032 std::string dictionary(NewSdchDictionary(kSampleDomain)); 1033 1034 std::string url_string = "http://" + kSampleDomain; 1035 1036 GURL url(url_string); 1037 EXPECT_TRUE(sdch_manager_->AddSdchDictionary(dictionary, url)); 1038 1039 std::string sdch_compressed(NewSdchCompressedData(dictionary)); 1040 1041 // Use Gzip to compress the sdch sdch_compressed data. 1042 std::string gzip_compressed_sdch = gzip_compress(sdch_compressed); 1043 1044 // Only claim to have non-encoded content, but really use the gzipped sdch 1045 // content. 1046 // System should automatically add the missing (optional) sdch,gzip. 1047 std::vector<Filter::FilterType> filter_types; 1048 1049 MockFilterContext filter_context; 1050 filter_context.SetMimeType("anything/mime"); 1051 filter_context.SetSdchResponse(true); 1052 Filter::FixupEncodingTypes(filter_context, &filter_types); 1053 ASSERT_EQ(filter_types.size(), 2u); 1054 EXPECT_EQ(filter_types[0], Filter::FILTER_TYPE_SDCH_POSSIBLE); 1055 EXPECT_EQ(filter_types[1], Filter::FILTER_TYPE_GZIP_HELPING_SDCH); 1056 1057 // First try with a large buffer (larger than test input, or compressed data). 1058 filter_context.SetURL(url); 1059 scoped_ptr<Filter> filter(Filter::Factory(filter_types, filter_context)); 1060 1061 1062 // Verify that chained filter is waiting for data. 1063 char tiny_output_buffer[10]; 1064 int tiny_output_size = sizeof(tiny_output_buffer); 1065 EXPECT_EQ(Filter::FILTER_NEED_MORE_DATA, 1066 filter->ReadData(tiny_output_buffer, &tiny_output_size)); 1067 1068 size_t feed_block_size = 100; 1069 size_t output_block_size = 100; 1070 std::string output; 1071 EXPECT_TRUE(FilterTestData(gzip_compressed_sdch, feed_block_size, 1072 output_block_size, filter.get(), &output)); 1073 EXPECT_EQ(output, expanded_); 1074 1075 // Next try with a tiny buffer to cover edge effects. 1076 filter.reset(Filter::Factory(filter_types, filter_context)); 1077 1078 feed_block_size = 1; 1079 output_block_size = 1; 1080 output.clear(); 1081 EXPECT_TRUE(FilterTestData(gzip_compressed_sdch, feed_block_size, 1082 output_block_size, filter.get(), &output)); 1083 EXPECT_EQ(output, expanded_); 1084 } 1085 1086 TEST_F(SdchFilterTest, AcceptGzipGzipSdchIfGzip) { 1087 // Construct a valid SDCH dictionary from a VCDIFF dictionary. 1088 const std::string kSampleDomain = "sdchtest.com"; 1089 std::string dictionary(NewSdchDictionary(kSampleDomain)); 1090 1091 std::string url_string = "http://" + kSampleDomain; 1092 1093 GURL url(url_string); 1094 EXPECT_TRUE(sdch_manager_->AddSdchDictionary(dictionary, url)); 1095 1096 std::string sdch_compressed(NewSdchCompressedData(dictionary)); 1097 1098 // Vodaphone (UK) Mobile Broadband provides double gzipped sdch with a content 1099 // encoding of merely gzip (apparently, only listing the extra level of 1100 // wrapper compression they added, but discarding the actual content encoding. 1101 // Use Gzip to double compress the sdch sdch_compressed data. 1102 std::string double_gzip_compressed_sdch = gzip_compress(gzip_compress( 1103 sdch_compressed)); 1104 1105 // Only claim to have gzip content, but really use the double gzipped sdch 1106 // content. 1107 // System should automatically add the missing (optional) sdch, gzip decoders. 1108 std::vector<Filter::FilterType> filter_types; 1109 filter_types.push_back(Filter::FILTER_TYPE_GZIP); 1110 1111 MockFilterContext filter_context; 1112 filter_context.SetMimeType("anything/mime"); 1113 filter_context.SetSdchResponse(true); 1114 Filter::FixupEncodingTypes(filter_context, &filter_types); 1115 ASSERT_EQ(filter_types.size(), 3u); 1116 EXPECT_EQ(filter_types[0], Filter::FILTER_TYPE_SDCH_POSSIBLE); 1117 EXPECT_EQ(filter_types[1], Filter::FILTER_TYPE_GZIP_HELPING_SDCH); 1118 EXPECT_EQ(filter_types[2], Filter::FILTER_TYPE_GZIP); 1119 1120 // First try with a large buffer (larger than test input, or compressed data). 1121 filter_context.SetURL(url); 1122 scoped_ptr<Filter> filter(Filter::Factory(filter_types, filter_context)); 1123 1124 1125 // Verify that chained filter is waiting for data. 1126 char tiny_output_buffer[10]; 1127 int tiny_output_size = sizeof(tiny_output_buffer); 1128 EXPECT_EQ(Filter::FILTER_NEED_MORE_DATA, 1129 filter->ReadData(tiny_output_buffer, &tiny_output_size)); 1130 1131 size_t feed_block_size = 100; 1132 size_t output_block_size = 100; 1133 std::string output; 1134 EXPECT_TRUE(FilterTestData(double_gzip_compressed_sdch, feed_block_size, 1135 output_block_size, filter.get(), &output)); 1136 EXPECT_EQ(output, expanded_); 1137 1138 // Next try with a tiny buffer to cover edge effects. 1139 filter.reset(Filter::Factory(filter_types, filter_context)); 1140 1141 feed_block_size = 1; 1142 output_block_size = 1; 1143 output.clear(); 1144 EXPECT_TRUE(FilterTestData(double_gzip_compressed_sdch, feed_block_size, 1145 output_block_size, filter.get(), &output)); 1146 EXPECT_EQ(output, expanded_); 1147 } 1148 1149 TEST_F(SdchFilterTest, DomainSupported) { 1150 GURL test_url("http://www.test.com"); 1151 GURL google_url("http://www.google.com"); 1152 1153 EXPECT_TRUE(SdchManager::sdch_enabled()); 1154 EXPECT_TRUE(SdchManager::Global()->IsInSupportedDomain(test_url)); 1155 sdch_manager_->EnableSdchSupport(".google.com"); 1156 EXPECT_FALSE(SdchManager::Global()->IsInSupportedDomain(test_url)); 1157 EXPECT_TRUE(SdchManager::Global()->IsInSupportedDomain(google_url)); 1158 } 1159 1160 TEST_F(SdchFilterTest, DomainBlacklisting) { 1161 GURL test_url("http://www.test.com"); 1162 GURL google_url("http://www.google.com"); 1163 1164 SdchManager::BlacklistDomain(test_url); 1165 EXPECT_FALSE(SdchManager::Global()->IsInSupportedDomain(test_url)); 1166 EXPECT_TRUE(SdchManager::Global()->IsInSupportedDomain(google_url)); 1167 1168 SdchManager::BlacklistDomain(google_url); 1169 EXPECT_FALSE(SdchManager::Global()->IsInSupportedDomain(google_url)); 1170 } 1171 1172 TEST_F(SdchFilterTest, DomainBlacklistingCaseSensitivity) { 1173 GURL test_url("http://www.TesT.com"); 1174 GURL test2_url("http://www.tEst.com"); 1175 1176 EXPECT_TRUE(SdchManager::Global()->IsInSupportedDomain(test_url)); 1177 EXPECT_TRUE(SdchManager::Global()->IsInSupportedDomain(test2_url)); 1178 SdchManager::BlacklistDomain(test_url); 1179 EXPECT_FALSE(SdchManager::Global()->IsInSupportedDomain(test2_url)); 1180 } 1181 1182 TEST_F(SdchFilterTest, BlacklistingReset) { 1183 GURL gurl("http://mytest.DoMain.com"); 1184 std::string domain(gurl.host()); 1185 1186 SdchManager::ClearBlacklistings(); 1187 EXPECT_EQ(SdchManager::BlackListDomainCount(domain), 0); 1188 EXPECT_EQ(SdchManager::BlacklistDomainExponential(domain), 0); 1189 EXPECT_TRUE(SdchManager::Global()->IsInSupportedDomain(gurl)); 1190 } 1191 1192 TEST_F(SdchFilterTest, BlacklistingSingleBlacklist) { 1193 GURL gurl("http://mytest.DoMain.com"); 1194 std::string domain(gurl.host()); 1195 SdchManager::ClearBlacklistings(); 1196 1197 SdchManager::Global()->BlacklistDomain(gurl); 1198 EXPECT_EQ(SdchManager::BlackListDomainCount(domain), 1); 1199 EXPECT_EQ(SdchManager::BlacklistDomainExponential(domain), 1); 1200 1201 // Check that any domain lookup reduces the blacklist counter. 1202 EXPECT_FALSE(SdchManager::Global()->IsInSupportedDomain(gurl)); 1203 EXPECT_EQ(SdchManager::BlackListDomainCount(domain), 0); 1204 EXPECT_TRUE(SdchManager::Global()->IsInSupportedDomain(gurl)); 1205 } 1206 1207 TEST_F(SdchFilterTest, BlacklistingExponential) { 1208 GURL gurl("http://mytest.DoMain.com"); 1209 std::string domain(gurl.host()); 1210 SdchManager::ClearBlacklistings(); 1211 1212 int exponential = 1; 1213 for (int i = 1; i < 100; ++i) { 1214 SdchManager::Global()->BlacklistDomain(gurl); 1215 EXPECT_EQ(SdchManager::BlacklistDomainExponential(domain), exponential); 1216 1217 EXPECT_EQ(SdchManager::BlackListDomainCount(domain), exponential); 1218 EXPECT_FALSE(SdchManager::Global()->IsInSupportedDomain(gurl)); 1219 EXPECT_EQ(SdchManager::BlackListDomainCount(domain), exponential - 1); 1220 1221 // Simulate a large number of domain checks (which eventually remove the 1222 // blacklisting). 1223 SdchManager::ClearDomainBlacklisting(domain); 1224 EXPECT_EQ(SdchManager::BlackListDomainCount(domain), 0); 1225 EXPECT_TRUE(SdchManager::Global()->IsInSupportedDomain(gurl)); 1226 1227 // Predict what exponential backoff will be. 1228 exponential = 1 + 2 * exponential; 1229 if (exponential < 0) 1230 exponential = INT_MAX; // We don't wrap. 1231 } 1232 } 1233 1234 TEST_F(SdchFilterTest, CanSetExactMatchDictionary) { 1235 std::string dictionary_domain("x.y.z.google.com"); 1236 std::string dictionary_text(NewSdchDictionary(dictionary_domain)); 1237 1238 // Perfect match should work. 1239 EXPECT_TRUE(sdch_manager_->AddSdchDictionary(dictionary_text, 1240 GURL("http://" + dictionary_domain))); 1241 } 1242 1243 TEST_F(SdchFilterTest, FailToSetDomainMismatchDictionary) { 1244 std::string dictionary_domain("x.y.z.google.com"); 1245 std::string dictionary_text(NewSdchDictionary(dictionary_domain)); 1246 1247 // Fail the "domain match" requirement. 1248 EXPECT_FALSE(sdch_manager_->AddSdchDictionary(dictionary_text, 1249 GURL("http://y.z.google.com"))); 1250 } 1251 1252 TEST_F(SdchFilterTest, FailToSetDotHostPrefixDomainDictionary) { 1253 std::string dictionary_domain("x.y.z.google.com"); 1254 std::string dictionary_text(NewSdchDictionary(dictionary_domain)); 1255 1256 // Fail the HD with D being the domain and H having a dot requirement. 1257 EXPECT_FALSE(sdch_manager_->AddSdchDictionary(dictionary_text, 1258 GURL("http://w.x.y.z.google.com"))); 1259 } 1260 1261 TEST_F(SdchFilterTest, FailToSetRepeatPrefixWithDotDictionary) { 1262 // Make sure that a prefix that matches the domain postfix won't confuse 1263 // the validation checks. 1264 std::string dictionary_domain("www.google.com"); 1265 std::string dictionary_text(NewSdchDictionary(dictionary_domain)); 1266 1267 // Fail the HD with D being the domain and H having a dot requirement. 1268 EXPECT_FALSE(sdch_manager_->AddSdchDictionary(dictionary_text, 1269 GURL("http://www.google.com.www.google.com"))); 1270 } 1271 1272 TEST_F(SdchFilterTest, CanSetLeadingDotDomainDictionary) { 1273 // Make sure that a prefix that matches the domain postfix won't confuse 1274 // the validation checks. 1275 std::string dictionary_domain(".google.com"); 1276 std::string dictionary_text(NewSdchDictionary(dictionary_domain)); 1277 1278 // Verify that a leading dot in the domain is acceptable, as long as the host 1279 // name does not contain any dots preceding the matched domain name. 1280 EXPECT_TRUE(sdch_manager_->AddSdchDictionary(dictionary_text, 1281 GURL("http://www.google.com"))); 1282 } 1283 1284 // Make sure the order of the tests is not helping us or confusing things. 1285 // See test CanSetExactMatchDictionary above for first try. 1286 TEST_F(SdchFilterTest, CanStillSetExactMatchDictionary) { 1287 std::string dictionary_domain("x.y.z.google.com"); 1288 std::string dictionary_text(NewSdchDictionary(dictionary_domain)); 1289 1290 // Perfect match should *STILL* work. 1291 EXPECT_TRUE(sdch_manager_->AddSdchDictionary(dictionary_text, 1292 GURL("http://" + dictionary_domain))); 1293 } 1294 1295 // Make sure the DOS protection precludes the addition of too many dictionaries. 1296 TEST_F(SdchFilterTest, TooManyDictionaries) { 1297 std::string dictionary_domain(".google.com"); 1298 std::string dictionary_text(NewSdchDictionary(dictionary_domain)); 1299 1300 size_t count = 0; 1301 while (count <= SdchManager::kMaxDictionaryCount + 1) { 1302 if (!sdch_manager_->AddSdchDictionary(dictionary_text, 1303 GURL("http://www.google.com"))) 1304 break; 1305 1306 dictionary_text += " "; // Create dictionary with different SHA signature. 1307 ++count; 1308 } 1309 EXPECT_EQ(SdchManager::kMaxDictionaryCount, count); 1310 } 1311 1312 TEST_F(SdchFilterTest, DictionaryNotTooLarge) { 1313 std::string dictionary_domain(".google.com"); 1314 std::string dictionary_text(NewSdchDictionary(dictionary_domain)); 1315 1316 dictionary_text.append( 1317 SdchManager::kMaxDictionarySize - dictionary_text.size(), ' '); 1318 EXPECT_TRUE(sdch_manager_->AddSdchDictionary(dictionary_text, 1319 GURL("http://" + dictionary_domain))); 1320 } 1321 1322 TEST_F(SdchFilterTest, DictionaryTooLarge) { 1323 std::string dictionary_domain(".google.com"); 1324 std::string dictionary_text(NewSdchDictionary(dictionary_domain)); 1325 1326 dictionary_text.append( 1327 SdchManager::kMaxDictionarySize + 1 - dictionary_text.size(), ' '); 1328 EXPECT_FALSE(sdch_manager_->AddSdchDictionary(dictionary_text, 1329 GURL("http://" + dictionary_domain))); 1330 } 1331 1332 TEST_F(SdchFilterTest, PathMatch) { 1333 bool (*PathMatch)(const std::string& path, const std::string& restriction) = 1334 SdchManager::Dictionary::PathMatch; 1335 // Perfect match is supported. 1336 EXPECT_TRUE(PathMatch("/search", "/search")); 1337 EXPECT_TRUE(PathMatch("/search/", "/search/")); 1338 1339 // Prefix only works if last character of restriction is a slash, or first 1340 // character in path after a match is a slash. Validate each case separately. 1341 1342 // Rely on the slash in the path (not at the end of the restriction). 1343 EXPECT_TRUE(PathMatch("/search/something", "/search")); 1344 EXPECT_TRUE(PathMatch("/search/s", "/search")); 1345 EXPECT_TRUE(PathMatch("/search/other", "/search")); 1346 EXPECT_TRUE(PathMatch("/search/something", "/search")); 1347 1348 // Rely on the slash at the end of the restriction. 1349 EXPECT_TRUE(PathMatch("/search/something", "/search/")); 1350 EXPECT_TRUE(PathMatch("/search/s", "/search/")); 1351 EXPECT_TRUE(PathMatch("/search/other", "/search/")); 1352 EXPECT_TRUE(PathMatch("/search/something", "/search/")); 1353 1354 // Make sure less that sufficient prefix match is false. 1355 EXPECT_FALSE(PathMatch("/sear", "/search")); 1356 EXPECT_FALSE(PathMatch("/", "/search")); 1357 EXPECT_FALSE(PathMatch("", "/search")); 1358 1359 // Add examples with several levels of direcories in the restriction. 1360 EXPECT_FALSE(PathMatch("/search/something", "search/s")); 1361 EXPECT_FALSE(PathMatch("/search/", "/search/s")); 1362 1363 // Make sure adding characters to path will also fail. 1364 EXPECT_FALSE(PathMatch("/searching", "/search/")); 1365 EXPECT_FALSE(PathMatch("/searching", "/search")); 1366 1367 // Make sure we're case sensitive. 1368 EXPECT_FALSE(PathMatch("/ABC", "/abc")); 1369 EXPECT_FALSE(PathMatch("/abc", "/ABC")); 1370 } 1371 1372 // The following are only applicable while we have a latency test in the code, 1373 // and can be removed when that functionality is stripped. 1374 TEST_F(SdchFilterTest, LatencyTestControls) { 1375 GURL url("http://www.google.com"); 1376 GURL url2("http://www.google2.com"); 1377 1378 // First make sure we default to false. 1379 EXPECT_FALSE(sdch_manager_->AllowLatencyExperiment(url)); 1380 EXPECT_FALSE(sdch_manager_->AllowLatencyExperiment(url2)); 1381 1382 // That we can set each to true. 1383 sdch_manager_->SetAllowLatencyExperiment(url, true); 1384 EXPECT_TRUE(sdch_manager_->AllowLatencyExperiment(url)); 1385 EXPECT_FALSE(sdch_manager_->AllowLatencyExperiment(url2)); 1386 1387 sdch_manager_->SetAllowLatencyExperiment(url2, true); 1388 EXPECT_TRUE(sdch_manager_->AllowLatencyExperiment(url)); 1389 EXPECT_TRUE(sdch_manager_->AllowLatencyExperiment(url2)); 1390 1391 // And can reset them to false. 1392 sdch_manager_->SetAllowLatencyExperiment(url, false); 1393 EXPECT_FALSE(sdch_manager_->AllowLatencyExperiment(url)); 1394 EXPECT_TRUE(sdch_manager_->AllowLatencyExperiment(url2)); 1395 1396 sdch_manager_->SetAllowLatencyExperiment(url2, false); 1397 EXPECT_FALSE(sdch_manager_->AllowLatencyExperiment(url)); 1398 EXPECT_FALSE(sdch_manager_->AllowLatencyExperiment(url2)); 1399 } 1400 1401 } // namespace net 1402