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