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