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