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