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