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