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