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