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