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.
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_http_job.h"
17 #include "testing/gtest/include/gtest/gtest.h"
18 #include "third_party/zlib/zlib.h"
22 //------------------------------------------------------------------------------
23 // Provide sample data and compression results with a sample VCDIFF dictionary.
24 // Note an SDCH dictionary has extra meta-data before the VCDIFF dictionary.
25 static const char kTestVcdiffDictionary
[] = "DictionaryFor"
26 "SdchCompression1SdchCompression2SdchCompression3SdchCompression\n";
27 // Pre-compression test data. Note that we pad with a lot of highly gzip
28 // compressible content to help to exercise the chaining pipeline. That is why
29 // there are a PILE of zeros at the start and end.
30 // This will ensure that gzip compressed data can be fed to the chain in one
31 // gulp, but (with careful selection of intermediate buffers) that it takes
32 // several sdch buffers worth of data to satisfy the sdch filter. See detailed
33 // CHECK() calls in FilterChaining test for specifics.
34 static const char kTestData
[] = "0000000000000000000000000000000000000000000000"
35 "0000000000000000000000000000TestData "
36 "SdchCompression1SdchCompression2SdchCompression3SdchCompression"
37 "00000000000000000000000000000000000000000000000000000000000000000000000000"
38 "000000000000000000000000000000000000000\n";
40 // Note SDCH compressed data will include a reference to the SDCH dictionary.
41 static const char kSdchCompressedTestData
[] =
42 "\326\303\304\0\0\001M\0\201S\202\004\0\201E\006\001"
43 "00000000000000000000000000000000000000000000000000000000000000000000000000"
44 "TestData 00000000000000000000000000000000000000000000000000000000000000000"
45 "000000000000000000000000000000000000000000000000\n\001S\023\077\001r\r";
47 //------------------------------------------------------------------------------
49 class SdchFilterTest
: public testing::Test
{
52 : test_vcdiff_dictionary_(kTestVcdiffDictionary
,
53 sizeof(kTestVcdiffDictionary
) - 1),
54 vcdiff_compressed_data_(kSdchCompressedTestData
,
55 sizeof(kSdchCompressedTestData
) - 1),
56 expanded_(kTestData
, sizeof(kTestData
) - 1),
57 sdch_manager_(new SdchManager
) {
60 std::string
NewSdchCompressedData(const std::string dictionary
);
62 const std::string test_vcdiff_dictionary_
;
63 const std::string vcdiff_compressed_data_
;
64 const std::string expanded_
; // Desired final, decompressed data.
66 scoped_ptr
<SdchManager
> sdch_manager_
; // A singleton database.
69 std::string
SdchFilterTest::NewSdchCompressedData(
70 const std::string dictionary
) {
71 std::string client_hash
;
72 std::string server_hash
;
73 SdchManager::GenerateHash(dictionary
, &client_hash
, &server_hash
);
75 // Build compressed data that refers to our dictionary.
76 std::string
compressed(server_hash
);
77 compressed
.append("\0", 1);
78 compressed
.append(vcdiff_compressed_data_
);
82 //------------------------------------------------------------------------------
85 TEST_F(SdchFilterTest
, Hashing
) {
86 std::string client_hash
, server_hash
;
87 std::string
dictionary("test contents");
88 SdchManager::GenerateHash(dictionary
, &client_hash
, &server_hash
);
90 EXPECT_EQ(client_hash
, "lMQBjS3P");
91 EXPECT_EQ(server_hash
, "MyciMVll");
95 //------------------------------------------------------------------------------
96 // Provide a generic helper function for trying to filter data.
97 // This function repeatedly calls the filter to process data, until the entire
98 // source is consumed. The return value from the filter is appended to output.
99 // This allows us to vary input and output block sizes in order to test for edge
100 // effects (boundary effects?) during the filtering process.
101 // This function provides data to the filter in blocks of no-more-than the
102 // specified input_block_length. It allows the filter to fill no more than
103 // output_buffer_length in any one call to proccess (a.k.a., Read) data, and
104 // concatenates all these little output blocks into the singular output string.
105 static bool FilterTestData(const std::string
& source
,
106 size_t input_block_length
,
107 const size_t output_buffer_length
,
108 Filter
* filter
, std::string
* output
) {
109 CHECK_GT(input_block_length
, 0u);
110 Filter::FilterStatus
status(Filter::FILTER_NEED_MORE_DATA
);
111 size_t source_index
= 0;
112 scoped_ptr
<char[]> output_buffer(new char[output_buffer_length
]);
113 size_t input_amount
= std::min(input_block_length
,
114 static_cast<size_t>(filter
->stream_buffer_size()));
117 int copy_amount
= std::min(input_amount
, source
.size() - source_index
);
118 if (copy_amount
> 0 && status
== Filter::FILTER_NEED_MORE_DATA
) {
119 memcpy(filter
->stream_buffer()->data(), source
.data() + source_index
,
121 filter
->FlushStreamBuffer(copy_amount
);
122 source_index
+= copy_amount
;
124 int buffer_length
= output_buffer_length
;
125 status
= filter
->ReadData(output_buffer
.get(), &buffer_length
);
126 output
->append(output_buffer
.get(), buffer_length
);
127 if (status
== Filter::FILTER_ERROR
)
129 // Callers assume that FILTER_OK with no output buffer means FILTER_DONE.
130 if (Filter::FILTER_OK
== status
&& 0 == buffer_length
)
132 if (copy_amount
== 0 && buffer_length
== 0)
136 //------------------------------------------------------------------------------
137 static std::string
NewSdchDictionary(const std::string
& domain
) {
138 std::string dictionary
;
139 if (!domain
.empty()) {
140 dictionary
.append("Domain: ");
141 dictionary
.append(domain
);
142 dictionary
.append("\n");
144 dictionary
.append("\n");
145 dictionary
.append(kTestVcdiffDictionary
, sizeof(kTestVcdiffDictionary
) - 1);
149 //------------------------------------------------------------------------------
151 TEST_F(SdchFilterTest
, EmptyInputOk
) {
152 std::vector
<Filter::FilterType
> filter_types
;
153 filter_types
.push_back(Filter::FILTER_TYPE_SDCH
);
154 char output_buffer
[20];
155 MockFilterContext filter_context
;
156 std::string
url_string("http://ignore.com");
157 filter_context
.SetURL(GURL(url_string
));
158 scoped_ptr
<Filter
> filter(Filter::Factory(filter_types
, filter_context
));
161 // With no input data, try to read output.
162 int output_bytes_or_buffer_size
= sizeof(output_buffer
);
163 Filter::FilterStatus status
= filter
->ReadData(output_buffer
,
164 &output_bytes_or_buffer_size
);
166 EXPECT_EQ(0, output_bytes_or_buffer_size
);
167 EXPECT_EQ(Filter::FILTER_NEED_MORE_DATA
, status
);
170 TEST_F(SdchFilterTest
, PassThroughWhenTentative
) {
171 std::vector
<Filter::FilterType
> filter_types
;
172 // Selective a tentative filter (which can fall back to pass through).
173 filter_types
.push_back(Filter::FILTER_TYPE_GZIP_HELPING_SDCH
);
174 char output_buffer
[20];
175 MockFilterContext filter_context
;
176 // Response code needs to be 200 to allow a pass through.
177 filter_context
.SetResponseCode(200);
178 std::string
url_string("http://ignore.com");
179 filter_context
.SetURL(GURL(url_string
));
180 scoped_ptr
<Filter
> filter(Filter::Factory(filter_types
, filter_context
));
182 // Supply enough data to force a pass-through mode..
183 std::string
non_gzip_content("not GZIPed data");
185 char* input_buffer
= filter
->stream_buffer()->data();
186 int input_buffer_size
= filter
->stream_buffer_size();
188 EXPECT_LT(static_cast<int>(non_gzip_content
.size()),
190 memcpy(input_buffer
, non_gzip_content
.data(),
191 non_gzip_content
.size());
192 filter
->FlushStreamBuffer(non_gzip_content
.size());
194 // Try to read output.
195 int output_bytes_or_buffer_size
= sizeof(output_buffer
);
196 Filter::FilterStatus status
= filter
->ReadData(output_buffer
,
197 &output_bytes_or_buffer_size
);
199 EXPECT_EQ(non_gzip_content
.size(),
200 static_cast<size_t>(output_bytes_or_buffer_size
));
201 ASSERT_GT(sizeof(output_buffer
),
202 static_cast<size_t>(output_bytes_or_buffer_size
));
203 output_buffer
[output_bytes_or_buffer_size
] = '\0';
204 EXPECT_TRUE(non_gzip_content
== output_buffer
);
205 EXPECT_EQ(Filter::FILTER_NEED_MORE_DATA
, status
);
208 TEST_F(SdchFilterTest
, RefreshBadReturnCode
) {
209 std::vector
<Filter::FilterType
> filter_types
;
210 // Selective a tentative filter (which can fall back to pass through).
211 filter_types
.push_back(Filter::FILTER_TYPE_SDCH_POSSIBLE
);
212 char output_buffer
[20];
213 MockFilterContext filter_context
;
214 // Response code needs to be 200 to allow a pass through.
215 filter_context
.SetResponseCode(403);
216 // Meta refresh will only appear for html content
217 filter_context
.SetMimeType("text/html");
218 std::string
url_string("http://ignore.com");
219 filter_context
.SetURL(GURL(url_string
));
220 scoped_ptr
<Filter
> filter(Filter::Factory(filter_types
, filter_context
));
222 // Supply enough data to force a pass-through mode, which means we have
223 // provided more than 9 characters that can't be a dictionary hash.
224 std::string
non_sdch_content("This is not SDCH");
226 char* input_buffer
= filter
->stream_buffer()->data();
227 int input_buffer_size
= filter
->stream_buffer_size();
229 EXPECT_LT(static_cast<int>(non_sdch_content
.size()),
231 memcpy(input_buffer
, non_sdch_content
.data(),
232 non_sdch_content
.size());
233 filter
->FlushStreamBuffer(non_sdch_content
.size());
235 // Try to read output.
236 int output_bytes_or_buffer_size
= sizeof(output_buffer
);
237 Filter::FilterStatus status
= filter
->ReadData(output_buffer
,
238 &output_bytes_or_buffer_size
);
240 // We should have read a long and complicated meta-refresh request.
241 EXPECT_TRUE(sizeof(output_buffer
) == output_bytes_or_buffer_size
);
242 // Check at least the prefix of the return.
243 EXPECT_EQ(0, strncmp(output_buffer
,
244 "<head><META HTTP-EQUIV=\"Refresh\" CONTENT=\"0\"></head>",
245 sizeof(output_buffer
)));
246 EXPECT_EQ(Filter::FILTER_OK
, status
);
249 TEST_F(SdchFilterTest
, ErrorOnBadReturnCode
) {
250 std::vector
<Filter::FilterType
> filter_types
;
251 // Selective a tentative filter (which can fall back to pass through).
252 filter_types
.push_back(Filter::FILTER_TYPE_SDCH_POSSIBLE
);
253 char output_buffer
[20];
254 MockFilterContext filter_context
;
255 // Response code needs to be 200 to allow a pass through.
256 filter_context
.SetResponseCode(403);
257 // Meta refresh will only appear for html content, so set to something else
258 // to induce an error (we can't meta refresh).
259 filter_context
.SetMimeType("anything");
260 std::string
url_string("http://ignore.com");
261 filter_context
.SetURL(GURL(url_string
));
262 scoped_ptr
<Filter
> filter(Filter::Factory(filter_types
, filter_context
));
264 // Supply enough data to force a pass-through mode, which means we have
265 // provided more than 9 characters that can't be a dictionary hash.
266 std::string
non_sdch_content("This is not SDCH");
268 char* input_buffer
= filter
->stream_buffer()->data();
269 int input_buffer_size
= filter
->stream_buffer_size();
271 EXPECT_LT(static_cast<int>(non_sdch_content
.size()),
273 memcpy(input_buffer
, non_sdch_content
.data(),
274 non_sdch_content
.size());
275 filter
->FlushStreamBuffer(non_sdch_content
.size());
277 // Try to read output.
278 int output_bytes_or_buffer_size
= sizeof(output_buffer
);
279 Filter::FilterStatus status
= filter
->ReadData(output_buffer
,
280 &output_bytes_or_buffer_size
);
282 EXPECT_EQ(0, output_bytes_or_buffer_size
);
283 EXPECT_EQ(Filter::FILTER_ERROR
, status
);
286 TEST_F(SdchFilterTest
, ErrorOnBadReturnCodeWithHtml
) {
287 std::vector
<Filter::FilterType
> filter_types
;
288 // Selective a tentative filter (which can fall back to pass through).
289 filter_types
.push_back(Filter::FILTER_TYPE_SDCH_POSSIBLE
);
290 char output_buffer
[20];
291 MockFilterContext filter_context
;
292 // Response code needs to be 200 to allow a pass through.
293 filter_context
.SetResponseCode(403);
294 // Meta refresh will only appear for html content
295 filter_context
.SetMimeType("text/html");
296 std::string
url_string("http://ignore.com");
297 filter_context
.SetURL(GURL(url_string
));
298 scoped_ptr
<Filter
> filter(Filter::Factory(filter_types
, filter_context
));
300 // Supply enough data to force a pass-through mode, which means we have
301 // provided more than 9 characters that can't be a dictionary hash.
302 std::string
non_sdch_content("This is not SDCH");
304 char* input_buffer
= filter
->stream_buffer()->data();
305 int input_buffer_size
= filter
->stream_buffer_size();
307 EXPECT_LT(static_cast<int>(non_sdch_content
.size()),
309 memcpy(input_buffer
, non_sdch_content
.data(),
310 non_sdch_content
.size());
311 filter
->FlushStreamBuffer(non_sdch_content
.size());
313 // Try to read output.
314 int output_bytes_or_buffer_size
= sizeof(output_buffer
);
315 Filter::FilterStatus status
= filter
->ReadData(output_buffer
,
316 &output_bytes_or_buffer_size
);
318 // We should have read a long and complicated meta-refresh request.
319 EXPECT_EQ(sizeof(output_buffer
),
320 static_cast<size_t>(output_bytes_or_buffer_size
));
321 // Check at least the prefix of the return.
322 EXPECT_EQ(0, strncmp(output_buffer
,
323 "<head><META HTTP-EQUIV=\"Refresh\" CONTENT=\"0\"></head>",
324 sizeof(output_buffer
)));
325 EXPECT_EQ(Filter::FILTER_OK
, status
);
328 TEST_F(SdchFilterTest
, BasicBadDictionary
) {
329 std::vector
<Filter::FilterType
> filter_types
;
330 filter_types
.push_back(Filter::FILTER_TYPE_SDCH
);
331 char output_buffer
[20];
332 MockFilterContext filter_context
;
333 std::string
url_string("http://ignore.com");
334 filter_context
.SetURL(GURL(url_string
));
335 scoped_ptr
<Filter
> filter(Filter::Factory(filter_types
, filter_context
));
337 // Supply bogus data (which doesn't yet specify a full dictionary hash).
338 // Dictionary hash is 8 characters followed by a null.
339 std::string
dictionary_hash_prefix("123");
341 char* input_buffer
= filter
->stream_buffer()->data();
342 int input_buffer_size
= filter
->stream_buffer_size();
344 EXPECT_LT(static_cast<int>(dictionary_hash_prefix
.size()),
346 memcpy(input_buffer
, dictionary_hash_prefix
.data(),
347 dictionary_hash_prefix
.size());
348 filter
->FlushStreamBuffer(dictionary_hash_prefix
.size());
350 // With less than a dictionary specifier, try to read output.
351 int output_bytes_or_buffer_size
= sizeof(output_buffer
);
352 Filter::FilterStatus status
= filter
->ReadData(output_buffer
,
353 &output_bytes_or_buffer_size
);
355 EXPECT_EQ(0, output_bytes_or_buffer_size
);
356 EXPECT_EQ(Filter::FILTER_NEED_MORE_DATA
, status
);
358 // Provide enough data to complete *a* hash, but it is bogus, and not in our
359 // list of dictionaries, so the filter should error out immediately.
360 std::string
dictionary_hash_postfix("4abcd\0", 6);
362 CHECK_LT(dictionary_hash_postfix
.size(),
363 static_cast<size_t>(input_buffer_size
));
364 memcpy(input_buffer
, dictionary_hash_postfix
.data(),
365 dictionary_hash_postfix
.size());
366 filter
->FlushStreamBuffer(dictionary_hash_postfix
.size());
368 // With a non-existant dictionary specifier, try to read output.
369 output_bytes_or_buffer_size
= sizeof(output_buffer
);
370 status
= filter
->ReadData(output_buffer
, &output_bytes_or_buffer_size
);
372 EXPECT_EQ(0, output_bytes_or_buffer_size
);
373 EXPECT_EQ(Filter::FILTER_ERROR
, status
);
375 EXPECT_FALSE(SdchManager::Global()->IsInSupportedDomain(GURL(url_string
)));
376 SdchManager::ClearBlacklistings();
377 EXPECT_TRUE(SdchManager::Global()->IsInSupportedDomain(GURL(url_string
)));
380 TEST_F(SdchFilterTest
, DictionaryAddOnce
) {
381 // Construct a valid SDCH dictionary from a VCDIFF dictionary.
382 const std::string kSampleDomain
= "sdchtest.com";
383 std::string
dictionary(NewSdchDictionary(kSampleDomain
));
385 std::string url_string
= "http://" + kSampleDomain
;
386 GURL
url(url_string
);
387 EXPECT_TRUE(sdch_manager_
->AddSdchDictionary(dictionary
, url
));
389 // Check we can't add it twice.
390 EXPECT_FALSE(sdch_manager_
->AddSdchDictionary(dictionary
, url
));
392 const std::string kSampleDomain2
= "sdchtest2.com";
394 // Construct a second SDCH dictionary from a VCDIFF dictionary.
395 std::string
dictionary2(NewSdchDictionary(kSampleDomain2
));
397 std::string url_string2
= "http://" + kSampleDomain2
;
398 GURL
url2(url_string2
);
399 EXPECT_TRUE(sdch_manager_
->AddSdchDictionary(dictionary2
, url2
));
402 TEST_F(SdchFilterTest
, BasicDictionary
) {
403 // Construct a valid SDCH dictionary from a VCDIFF dictionary.
404 const std::string kSampleDomain
= "sdchtest.com";
405 std::string
dictionary(NewSdchDictionary(kSampleDomain
));
407 std::string url_string
= "http://" + kSampleDomain
;
409 GURL
url(url_string
);
410 EXPECT_TRUE(sdch_manager_
->AddSdchDictionary(dictionary
, url
));
412 std::string
compressed(NewSdchCompressedData(dictionary
));
414 std::vector
<Filter::FilterType
> filter_types
;
415 filter_types
.push_back(Filter::FILTER_TYPE_SDCH
);
417 MockFilterContext filter_context
;
418 filter_context
.SetURL(url
);
420 scoped_ptr
<Filter
> filter(Filter::Factory(filter_types
, filter_context
));
422 size_t feed_block_size
= 100;
423 size_t output_block_size
= 100;
425 EXPECT_TRUE(FilterTestData(compressed
, feed_block_size
, output_block_size
,
426 filter
.get(), &output
));
427 EXPECT_EQ(output
, expanded_
);
429 // Decode with really small buffers (size 1) to check for edge effects.
430 filter
.reset(Filter::Factory(filter_types
, filter_context
));
433 output_block_size
= 1;
435 EXPECT_TRUE(FilterTestData(compressed
, feed_block_size
, output_block_size
,
436 filter
.get(), &output
));
437 EXPECT_EQ(output
, expanded_
);
440 TEST_F(SdchFilterTest
, NoDecodeHttps
) {
441 // Construct a valid SDCH dictionary from a VCDIFF dictionary.
442 const std::string kSampleDomain
= "sdchtest.com";
443 std::string
dictionary(NewSdchDictionary(kSampleDomain
));
445 std::string url_string
= "http://" + kSampleDomain
;
447 GURL
url(url_string
);
448 EXPECT_TRUE(sdch_manager_
->AddSdchDictionary(dictionary
, url
));
450 std::string
compressed(NewSdchCompressedData(dictionary
));
452 std::vector
<Filter::FilterType
> filter_types
;
453 filter_types
.push_back(Filter::FILTER_TYPE_SDCH
);
455 MockFilterContext filter_context
;
456 filter_context
.SetURL(GURL("https://" + kSampleDomain
));
457 scoped_ptr
<Filter
> filter(Filter::Factory(filter_types
, filter_context
));
459 const size_t feed_block_size(100);
460 const size_t output_block_size(100);
463 EXPECT_FALSE(FilterTestData(compressed
, feed_block_size
, output_block_size
,
464 filter
.get(), &output
));
467 // Current failsafe TODO/hack refuses to decode any content that doesn't use
468 // http as the scheme (see use of DICTIONARY_SELECTED_FOR_NON_HTTP).
469 // The following tests this blockage. Note that blacklisting results, so we
470 // we need separate tests for each of these.
471 TEST_F(SdchFilterTest
, NoDecodeFtp
) {
472 // Construct a valid SDCH dictionary from a VCDIFF dictionary.
473 const std::string kSampleDomain
= "sdchtest.com";
474 std::string
dictionary(NewSdchDictionary(kSampleDomain
));
476 std::string url_string
= "http://" + kSampleDomain
;
478 GURL
url(url_string
);
479 EXPECT_TRUE(sdch_manager_
->AddSdchDictionary(dictionary
, url
));
481 std::string
compressed(NewSdchCompressedData(dictionary
));
483 std::vector
<Filter::FilterType
> filter_types
;
484 filter_types
.push_back(Filter::FILTER_TYPE_SDCH
);
486 MockFilterContext filter_context
;
487 filter_context
.SetURL(GURL("ftp://" + kSampleDomain
));
488 scoped_ptr
<Filter
> filter(Filter::Factory(filter_types
, filter_context
));
490 const size_t feed_block_size(100);
491 const size_t output_block_size(100);
494 EXPECT_FALSE(FilterTestData(compressed
, feed_block_size
, output_block_size
,
495 filter
.get(), &output
));
498 TEST_F(SdchFilterTest
, NoDecodeFileColon
) {
499 // Construct a valid SDCH dictionary from a VCDIFF dictionary.
500 const std::string kSampleDomain
= "sdchtest.com";
501 std::string
dictionary(NewSdchDictionary(kSampleDomain
));
503 std::string url_string
= "http://" + kSampleDomain
;
505 GURL
url(url_string
);
506 EXPECT_TRUE(sdch_manager_
->AddSdchDictionary(dictionary
, url
));
508 std::string
compressed(NewSdchCompressedData(dictionary
));
510 std::vector
<Filter::FilterType
> filter_types
;
511 filter_types
.push_back(Filter::FILTER_TYPE_SDCH
);
513 MockFilterContext filter_context
;
514 filter_context
.SetURL(GURL("file://" + kSampleDomain
));
515 scoped_ptr
<Filter
> filter(Filter::Factory(filter_types
, filter_context
));
517 const size_t feed_block_size(100);
518 const size_t output_block_size(100);
521 EXPECT_FALSE(FilterTestData(compressed
, feed_block_size
, output_block_size
,
522 filter
.get(), &output
));
525 TEST_F(SdchFilterTest
, NoDecodeAboutColon
) {
526 // Construct a valid SDCH dictionary from a VCDIFF dictionary.
527 const std::string kSampleDomain
= "sdchtest.com";
528 std::string
dictionary(NewSdchDictionary(kSampleDomain
));
530 std::string url_string
= "http://" + kSampleDomain
;
532 GURL
url(url_string
);
533 EXPECT_TRUE(sdch_manager_
->AddSdchDictionary(dictionary
, url
));
535 std::string
compressed(NewSdchCompressedData(dictionary
));
537 std::vector
<Filter::FilterType
> filter_types
;
538 filter_types
.push_back(Filter::FILTER_TYPE_SDCH
);
540 MockFilterContext filter_context
;
541 filter_context
.SetURL(GURL("about://" + kSampleDomain
));
542 scoped_ptr
<Filter
> filter(Filter::Factory(filter_types
, filter_context
));
544 const size_t feed_block_size(100);
545 const size_t output_block_size(100);
548 EXPECT_FALSE(FilterTestData(compressed
, feed_block_size
, output_block_size
,
549 filter
.get(), &output
));
552 TEST_F(SdchFilterTest
, NoDecodeJavaScript
) {
553 // Construct a valid SDCH dictionary from a VCDIFF dictionary.
554 const std::string kSampleDomain
= "sdchtest.com";
555 std::string
dictionary(NewSdchDictionary(kSampleDomain
));
557 std::string url_string
= "http://" + kSampleDomain
;
559 GURL
url(url_string
);
560 EXPECT_TRUE(sdch_manager_
->AddSdchDictionary(dictionary
, url
));
562 std::string
compressed(NewSdchCompressedData(dictionary
));
564 std::vector
<Filter::FilterType
> filter_types
;
565 filter_types
.push_back(Filter::FILTER_TYPE_SDCH
);
567 MockFilterContext filter_context
;
568 filter_context
.SetURL(GURL("javascript://" + kSampleDomain
));
569 scoped_ptr
<Filter
> filter(Filter::Factory(filter_types
, filter_context
));
571 const size_t feed_block_size(100);
572 const size_t output_block_size(100);
575 EXPECT_FALSE(FilterTestData(compressed
, feed_block_size
, output_block_size
,
576 filter
.get(), &output
));
579 TEST_F(SdchFilterTest
, CanStillDecodeHttp
) {
580 // Construct a valid SDCH dictionary from a VCDIFF dictionary.
581 const std::string kSampleDomain
= "sdchtest.com";
582 std::string
dictionary(NewSdchDictionary(kSampleDomain
));
584 std::string url_string
= "http://" + kSampleDomain
;
586 GURL
url(url_string
);
587 EXPECT_TRUE(sdch_manager_
->AddSdchDictionary(dictionary
, url
));
589 std::string
compressed(NewSdchCompressedData(dictionary
));
591 std::vector
<Filter::FilterType
> filter_types
;
592 filter_types
.push_back(Filter::FILTER_TYPE_SDCH
);
594 MockFilterContext filter_context
;
595 filter_context
.SetURL(GURL("http://" + kSampleDomain
));
596 scoped_ptr
<Filter
> filter(Filter::Factory(filter_types
, filter_context
));
598 const size_t feed_block_size(100);
599 const size_t output_block_size(100);
602 EXPECT_TRUE(FilterTestData(compressed
, feed_block_size
, output_block_size
,
603 filter
.get(), &output
));
606 TEST_F(SdchFilterTest
, CrossDomainDictionaryUse
) {
607 // Construct a valid SDCH dictionary from a VCDIFF dictionary.
608 const std::string kSampleDomain
= "sdchtest.com";
609 std::string
dictionary(NewSdchDictionary(kSampleDomain
));
611 std::string url_string
= "http://" + kSampleDomain
;
613 GURL
url(url_string
);
614 EXPECT_TRUE(sdch_manager_
->AddSdchDictionary(dictionary
, url
));
616 std::string
compressed(NewSdchCompressedData(dictionary
));
618 std::vector
<Filter::FilterType
> filter_types
;
619 filter_types
.push_back(Filter::FILTER_TYPE_SDCH
);
621 // Decode with content arriving from the "wrong" domain.
622 // This tests SdchManager::CanSet().
623 MockFilterContext filter_context
;
624 GURL
wrong_domain_url("http://www.wrongdomain.com");
625 filter_context
.SetURL(wrong_domain_url
);
626 scoped_ptr
<Filter
> filter(Filter::Factory(filter_types
, filter_context
));
628 size_t feed_block_size
= 100;
629 size_t output_block_size
= 100;
631 EXPECT_FALSE(FilterTestData(compressed
, feed_block_size
, output_block_size
,
632 filter
.get(), &output
));
633 EXPECT_EQ(output
.size(), 0u); // No output written.
635 EXPECT_TRUE(SdchManager::Global()->IsInSupportedDomain(GURL(url_string
)));
636 EXPECT_FALSE(SdchManager::Global()->IsInSupportedDomain(wrong_domain_url
));
637 SdchManager::ClearBlacklistings();
638 EXPECT_TRUE(SdchManager::Global()->IsInSupportedDomain(wrong_domain_url
));
641 TEST_F(SdchFilterTest
, DictionaryPathValidation
) {
642 // Construct a valid SDCH dictionary from a VCDIFF dictionary.
643 const std::string kSampleDomain
= "sdchtest.com";
644 std::string
dictionary(NewSdchDictionary(kSampleDomain
));
646 std::string url_string
= "http://" + kSampleDomain
;
648 GURL
url(url_string
);
649 EXPECT_TRUE(sdch_manager_
->AddSdchDictionary(dictionary
, url
));
651 // Create a dictionary with a path restriction, by prefixing dictionary.
652 const std::string
path("/special_path/bin");
653 std::string
dictionary_with_path("Path: " + path
+ "\n");
654 dictionary_with_path
.append(dictionary
);
655 EXPECT_TRUE(sdch_manager_
->AddSdchDictionary(dictionary_with_path
, url
));
657 std::string
compressed_for_path(NewSdchCompressedData(dictionary_with_path
));
659 std::vector
<Filter::FilterType
> filter_types
;
660 filter_types
.push_back(Filter::FILTER_TYPE_SDCH
);
662 // Test decode the path data, arriving from a valid path.
663 MockFilterContext filter_context
;
664 filter_context
.SetURL(GURL(url_string
+ path
));
665 scoped_ptr
<Filter
> filter(Filter::Factory(filter_types
, filter_context
));
667 size_t feed_block_size
= 100;
668 size_t output_block_size
= 100;
671 EXPECT_TRUE(FilterTestData(compressed_for_path
, feed_block_size
,
672 output_block_size
, filter
.get(), &output
));
673 EXPECT_EQ(output
, expanded_
);
675 // Test decode the path data, arriving from a invalid path.
676 filter_context
.SetURL(GURL(url_string
));
677 filter
.reset(Filter::Factory(filter_types
, filter_context
));
679 feed_block_size
= 100;
680 output_block_size
= 100;
682 EXPECT_FALSE(FilterTestData(compressed_for_path
, feed_block_size
,
683 output_block_size
, filter
.get(), &output
));
684 EXPECT_EQ(output
.size(), 0u); // No output written.
686 EXPECT_FALSE(SdchManager::Global()->IsInSupportedDomain(GURL(url_string
)));
687 SdchManager::ClearBlacklistings();
688 EXPECT_TRUE(SdchManager::Global()->IsInSupportedDomain(GURL(url_string
)));
691 TEST_F(SdchFilterTest
, DictionaryPortValidation
) {
692 // Construct a valid SDCH dictionary from a VCDIFF dictionary.
693 const std::string kSampleDomain
= "sdchtest.com";
694 std::string
dictionary(NewSdchDictionary(kSampleDomain
));
696 std::string url_string
= "http://" + kSampleDomain
;
698 GURL
url(url_string
);
699 EXPECT_TRUE(sdch_manager_
->AddSdchDictionary(dictionary
, url
));
702 // Create a dictionary with a port restriction, by prefixing old dictionary.
703 const std::string
port("502");
704 std::string
dictionary_with_port("Port: " + port
+ "\n");
705 dictionary_with_port
.append("Port: 80\n"); // Add default port.
706 dictionary_with_port
.append(dictionary
);
707 EXPECT_TRUE(sdch_manager_
->AddSdchDictionary(dictionary_with_port
,
708 GURL(url_string
+ ":" + port
)));
710 std::string
compressed_for_port(NewSdchCompressedData(dictionary_with_port
));
712 std::vector
<Filter::FilterType
> filter_types
;
713 filter_types
.push_back(Filter::FILTER_TYPE_SDCH
);
715 // Test decode the port data, arriving from a valid port.
716 MockFilterContext filter_context
;
717 filter_context
.SetURL(GURL(url_string
+ ":" + port
));
718 scoped_ptr
<Filter
> filter(Filter::Factory(filter_types
, filter_context
));
720 size_t feed_block_size
= 100;
721 size_t output_block_size
= 100;
723 EXPECT_TRUE(FilterTestData(compressed_for_port
, feed_block_size
,
724 output_block_size
, filter
.get(), &output
));
725 EXPECT_EQ(output
, expanded_
);
727 // Test decode the port data, arriving from a valid (default) port.
728 filter_context
.SetURL(GURL(url_string
)); // Default port.
729 filter
.reset(Filter::Factory(filter_types
, filter_context
));
731 feed_block_size
= 100;
732 output_block_size
= 100;
734 EXPECT_TRUE(FilterTestData(compressed_for_port
, feed_block_size
,
735 output_block_size
, filter
.get(), &output
));
736 EXPECT_EQ(output
, expanded_
);
738 // Test decode the port data, arriving from a invalid port.
739 filter_context
.SetURL(GURL(url_string
+ ":" + port
+ "1"));
740 filter
.reset(Filter::Factory(filter_types
, filter_context
));
742 feed_block_size
= 100;
743 output_block_size
= 100;
745 EXPECT_FALSE(FilterTestData(compressed_for_port
, feed_block_size
,
746 output_block_size
, filter
.get(), &output
));
747 EXPECT_EQ(output
.size(), 0u); // No output written.
749 EXPECT_FALSE(SdchManager::Global()->IsInSupportedDomain(GURL(url_string
)));
750 SdchManager::ClearBlacklistings();
751 EXPECT_TRUE(SdchManager::Global()->IsInSupportedDomain(GURL(url_string
)));
754 //------------------------------------------------------------------------------
755 // Helper function to perform gzip compression of data.
757 static std::string
gzip_compress(const std::string
&input
) {
758 z_stream zlib_stream
;
759 memset(&zlib_stream
, 0, sizeof(zlib_stream
));
763 code
= deflateInit2(&zlib_stream
, Z_DEFAULT_COMPRESSION
, Z_DEFLATED
,
768 CHECK_EQ(Z_OK
, code
);
770 // Fill in zlib control block
771 zlib_stream
.next_in
= bit_cast
<Bytef
*>(input
.data());
772 zlib_stream
.avail_in
= input
.size();
774 // Assume we can compress into similar buffer (add 100 bytes to be sure).
775 size_t gzip_compressed_length
= zlib_stream
.avail_in
+ 100;
776 scoped_ptr
<char[]> gzip_compressed(new char[gzip_compressed_length
]);
777 zlib_stream
.next_out
= bit_cast
<Bytef
*>(gzip_compressed
.get());
778 zlib_stream
.avail_out
= gzip_compressed_length
;
780 // The GZIP header (see RFC 1952):
781 // +---+---+---+---+---+---+---+---+---+---+
782 // |ID1|ID2|CM |FLG| MTIME |XFL|OS |
783 // +---+---+---+---+---+---+---+---+---+---+
786 // CM \010 (compression method == DEFLATE)
787 // FLG \000 (special flags that we do not support)
788 // MTIME Unix format modification time (0 means not available)
789 // XFL 2-4? DEFLATE flags
790 // OS ???? Operating system indicator (255 means unknown)
792 // Header value we generate:
793 const char kGZipHeader
[] = { '\037', '\213', '\010', '\000', '\000',
794 '\000', '\000', '\000', '\002', '\377' };
795 CHECK_GT(zlib_stream
.avail_out
, sizeof(kGZipHeader
));
796 memcpy(zlib_stream
.next_out
, kGZipHeader
, sizeof(kGZipHeader
));
797 zlib_stream
.next_out
+= sizeof(kGZipHeader
);
798 zlib_stream
.avail_out
-= sizeof(kGZipHeader
);
801 code
= deflate(&zlib_stream
, Z_FINISH
);
802 gzip_compressed_length
-= zlib_stream
.avail_out
;
803 std::string
compressed(gzip_compressed
.get(), gzip_compressed_length
);
804 deflateEnd(&zlib_stream
);
808 //------------------------------------------------------------------------------
810 class SdchFilterChainingTest
{
812 static Filter
* Factory(const std::vector
<Filter::FilterType
>& types
,
813 const FilterContext
& context
, int size
) {
814 return Filter::FactoryForTests(types
, context
, size
);
818 // Test that filters can be cascaded (chained) so that the output of one filter
819 // is processed by the next one. This is most critical for SDCH, which is
820 // routinely followed by gzip (during encoding). The filter we'll test for will
821 // do the gzip decoding first, and then decode the SDCH content.
822 TEST_F(SdchFilterTest
, FilterChaining
) {
823 // Construct a valid SDCH dictionary from a VCDIFF dictionary.
824 const std::string kSampleDomain
= "sdchtest.com";
825 std::string
dictionary(NewSdchDictionary(kSampleDomain
));
827 std::string url_string
= "http://" + kSampleDomain
;
829 GURL
url(url_string
);
830 EXPECT_TRUE(sdch_manager_
->AddSdchDictionary(dictionary
, url
));
832 std::string
sdch_compressed(NewSdchCompressedData(dictionary
));
834 // Use Gzip to compress the sdch sdch_compressed data.
835 std::string gzip_compressed_sdch
= gzip_compress(sdch_compressed
);
837 // Construct a chained filter.
838 std::vector
<Filter::FilterType
> filter_types
;
839 filter_types
.push_back(Filter::FILTER_TYPE_SDCH
);
840 filter_types
.push_back(Filter::FILTER_TYPE_GZIP
);
842 // First try with a large buffer (larger than test input, or compressed data).
843 const size_t kLargeInputBufferSize(1000); // Used internally in filters.
844 CHECK_GT(kLargeInputBufferSize
, gzip_compressed_sdch
.size());
845 CHECK_GT(kLargeInputBufferSize
, sdch_compressed
.size());
846 CHECK_GT(kLargeInputBufferSize
, expanded_
.size());
847 MockFilterContext filter_context
;
848 filter_context
.SetURL(url
);
849 scoped_ptr
<Filter
> filter(
850 SdchFilterChainingTest::Factory(filter_types
, filter_context
,
851 kLargeInputBufferSize
));
852 EXPECT_EQ(static_cast<int>(kLargeInputBufferSize
),
853 filter
->stream_buffer_size());
855 // Verify that chained filter is waiting for data.
856 char tiny_output_buffer
[10];
857 int tiny_output_size
= sizeof(tiny_output_buffer
);
858 EXPECT_EQ(Filter::FILTER_NEED_MORE_DATA
,
859 filter
->ReadData(tiny_output_buffer
, &tiny_output_size
));
861 // Make chain process all data.
862 size_t feed_block_size
= kLargeInputBufferSize
;
863 size_t output_block_size
= kLargeInputBufferSize
;
865 EXPECT_TRUE(FilterTestData(gzip_compressed_sdch
, feed_block_size
,
866 output_block_size
, filter
.get(), &output
));
867 EXPECT_EQ(output
, expanded_
);
869 // Next try with a mid-sized internal buffer size.
870 const size_t kMidSizedInputBufferSize(100);
871 // Buffer should be big enough to swallow whole gzip content.
872 CHECK_GT(kMidSizedInputBufferSize
, gzip_compressed_sdch
.size());
873 // Buffer should be small enough that entire SDCH content can't fit.
874 // We'll go even further, and force the chain to flush the buffer between the
875 // two filters more than once (that is why we multiply by 2).
876 CHECK_LT(kMidSizedInputBufferSize
* 2, sdch_compressed
.size());
877 filter_context
.SetURL(url
);
879 SdchFilterChainingTest::Factory(filter_types
, filter_context
,
880 kMidSizedInputBufferSize
));
881 EXPECT_EQ(static_cast<int>(kMidSizedInputBufferSize
),
882 filter
->stream_buffer_size());
884 feed_block_size
= kMidSizedInputBufferSize
;
885 output_block_size
= kMidSizedInputBufferSize
;
887 EXPECT_TRUE(FilterTestData(gzip_compressed_sdch
, feed_block_size
,
888 output_block_size
, filter
.get(), &output
));
889 EXPECT_EQ(output
, expanded_
);
891 // Next try with a tiny input and output buffer to cover edge effects.
892 filter
.reset(SdchFilterChainingTest::Factory(filter_types
, filter_context
,
893 kLargeInputBufferSize
));
894 EXPECT_EQ(static_cast<int>(kLargeInputBufferSize
),
895 filter
->stream_buffer_size());
898 output_block_size
= 1;
900 EXPECT_TRUE(FilterTestData(gzip_compressed_sdch
, feed_block_size
,
901 output_block_size
, filter
.get(), &output
));
902 EXPECT_EQ(output
, expanded_
);
905 TEST_F(SdchFilterTest
, DefaultGzipIfSdch
) {
906 // Construct a valid SDCH dictionary from a VCDIFF dictionary.
907 const std::string kSampleDomain
= "sdchtest.com";
908 std::string
dictionary(NewSdchDictionary(kSampleDomain
));
910 std::string url_string
= "http://" + kSampleDomain
;
912 GURL
url(url_string
);
913 EXPECT_TRUE(sdch_manager_
->AddSdchDictionary(dictionary
, url
));
915 std::string
sdch_compressed(NewSdchCompressedData(dictionary
));
917 // Use Gzip to compress the sdch sdch_compressed data.
918 std::string gzip_compressed_sdch
= gzip_compress(sdch_compressed
);
920 // Only claim to have sdch content, but really use the gzipped sdch content.
921 // System should automatically add the missing (optional) gzip.
922 std::vector
<Filter::FilterType
> filter_types
;
923 filter_types
.push_back(Filter::FILTER_TYPE_SDCH
);
925 MockFilterContext filter_context
;
926 filter_context
.SetMimeType("anything/mime");
927 filter_context
.SetSdchResponse(true);
928 Filter::FixupEncodingTypes(filter_context
, &filter_types
);
929 ASSERT_EQ(filter_types
.size(), 2u);
930 EXPECT_EQ(filter_types
[0], Filter::FILTER_TYPE_SDCH
);
931 EXPECT_EQ(filter_types
[1], Filter::FILTER_TYPE_GZIP_HELPING_SDCH
);
933 // First try with a large buffer (larger than test input, or compressed data).
934 filter_context
.SetURL(url
);
935 scoped_ptr
<Filter
> filter(Filter::Factory(filter_types
, filter_context
));
938 // Verify that chained filter is waiting for data.
939 char tiny_output_buffer
[10];
940 int tiny_output_size
= sizeof(tiny_output_buffer
);
941 EXPECT_EQ(Filter::FILTER_NEED_MORE_DATA
,
942 filter
->ReadData(tiny_output_buffer
, &tiny_output_size
));
944 size_t feed_block_size
= 100;
945 size_t output_block_size
= 100;
947 EXPECT_TRUE(FilterTestData(gzip_compressed_sdch
, feed_block_size
,
948 output_block_size
, filter
.get(), &output
));
949 EXPECT_EQ(output
, expanded_
);
951 // Next try with a tiny buffer to cover edge effects.
952 filter
.reset(Filter::Factory(filter_types
, filter_context
));
955 output_block_size
= 1;
957 EXPECT_TRUE(FilterTestData(gzip_compressed_sdch
, feed_block_size
,
958 output_block_size
, filter
.get(), &output
));
959 EXPECT_EQ(output
, expanded_
);
962 TEST_F(SdchFilterTest
, AcceptGzipSdchIfGzip
) {
963 // Construct a valid SDCH dictionary from a VCDIFF dictionary.
964 const std::string kSampleDomain
= "sdchtest.com";
965 std::string
dictionary(NewSdchDictionary(kSampleDomain
));
967 std::string url_string
= "http://" + kSampleDomain
;
969 GURL
url(url_string
);
970 EXPECT_TRUE(sdch_manager_
->AddSdchDictionary(dictionary
, url
));
972 std::string
sdch_compressed(NewSdchCompressedData(dictionary
));
974 // Use Gzip to compress the sdch sdch_compressed data.
975 std::string gzip_compressed_sdch
= gzip_compress(sdch_compressed
);
977 // Some proxies strip the content encoding statement down to a mere gzip, but
978 // pass through the original content (with full sdch,gzip encoding).
979 // Only claim to have gzip content, but really use the gzipped sdch content.
980 // System should automatically add the missing (optional) sdch.
981 std::vector
<Filter::FilterType
> filter_types
;
982 filter_types
.push_back(Filter::FILTER_TYPE_GZIP
);
984 MockFilterContext filter_context
;
985 filter_context
.SetMimeType("anything/mime");
986 filter_context
.SetSdchResponse(true);
987 Filter::FixupEncodingTypes(filter_context
, &filter_types
);
988 ASSERT_EQ(filter_types
.size(), 3u);
989 EXPECT_EQ(filter_types
[0], Filter::FILTER_TYPE_SDCH_POSSIBLE
);
990 EXPECT_EQ(filter_types
[1], Filter::FILTER_TYPE_GZIP_HELPING_SDCH
);
991 EXPECT_EQ(filter_types
[2], Filter::FILTER_TYPE_GZIP
);
993 // First try with a large buffer (larger than test input, or compressed data).
994 filter_context
.SetURL(url
);
995 scoped_ptr
<Filter
> filter(Filter::Factory(filter_types
, filter_context
));
998 // Verify that chained filter is waiting for data.
999 char tiny_output_buffer
[10];
1000 int tiny_output_size
= sizeof(tiny_output_buffer
);
1001 EXPECT_EQ(Filter::FILTER_NEED_MORE_DATA
,
1002 filter
->ReadData(tiny_output_buffer
, &tiny_output_size
));
1004 size_t feed_block_size
= 100;
1005 size_t output_block_size
= 100;
1007 EXPECT_TRUE(FilterTestData(gzip_compressed_sdch
, feed_block_size
,
1008 output_block_size
, filter
.get(), &output
));
1009 EXPECT_EQ(output
, expanded_
);
1011 // Next try with a tiny buffer to cover edge effects.
1012 filter
.reset(Filter::Factory(filter_types
, filter_context
));
1014 feed_block_size
= 1;
1015 output_block_size
= 1;
1017 EXPECT_TRUE(FilterTestData(gzip_compressed_sdch
, feed_block_size
,
1018 output_block_size
, filter
.get(), &output
));
1019 EXPECT_EQ(output
, expanded_
);
1022 TEST_F(SdchFilterTest
, DefaultSdchGzipIfEmpty
) {
1023 // Construct a valid SDCH dictionary from a VCDIFF dictionary.
1024 const std::string kSampleDomain
= "sdchtest.com";
1025 std::string
dictionary(NewSdchDictionary(kSampleDomain
));
1027 std::string url_string
= "http://" + kSampleDomain
;
1029 GURL
url(url_string
);
1030 EXPECT_TRUE(sdch_manager_
->AddSdchDictionary(dictionary
, url
));
1032 std::string
sdch_compressed(NewSdchCompressedData(dictionary
));
1034 // Use Gzip to compress the sdch sdch_compressed data.
1035 std::string gzip_compressed_sdch
= gzip_compress(sdch_compressed
);
1037 // Only claim to have non-encoded content, but really use the gzipped sdch
1039 // System should automatically add the missing (optional) sdch,gzip.
1040 std::vector
<Filter::FilterType
> filter_types
;
1042 MockFilterContext filter_context
;
1043 filter_context
.SetMimeType("anything/mime");
1044 filter_context
.SetSdchResponse(true);
1045 Filter::FixupEncodingTypes(filter_context
, &filter_types
);
1046 ASSERT_EQ(filter_types
.size(), 2u);
1047 EXPECT_EQ(filter_types
[0], Filter::FILTER_TYPE_SDCH_POSSIBLE
);
1048 EXPECT_EQ(filter_types
[1], Filter::FILTER_TYPE_GZIP_HELPING_SDCH
);
1050 // First try with a large buffer (larger than test input, or compressed data).
1051 filter_context
.SetURL(url
);
1052 scoped_ptr
<Filter
> filter(Filter::Factory(filter_types
, filter_context
));
1055 // Verify that chained filter is waiting for data.
1056 char tiny_output_buffer
[10];
1057 int tiny_output_size
= sizeof(tiny_output_buffer
);
1058 EXPECT_EQ(Filter::FILTER_NEED_MORE_DATA
,
1059 filter
->ReadData(tiny_output_buffer
, &tiny_output_size
));
1061 size_t feed_block_size
= 100;
1062 size_t output_block_size
= 100;
1064 EXPECT_TRUE(FilterTestData(gzip_compressed_sdch
, feed_block_size
,
1065 output_block_size
, filter
.get(), &output
));
1066 EXPECT_EQ(output
, expanded_
);
1068 // Next try with a tiny buffer to cover edge effects.
1069 filter
.reset(Filter::Factory(filter_types
, filter_context
));
1071 feed_block_size
= 1;
1072 output_block_size
= 1;
1074 EXPECT_TRUE(FilterTestData(gzip_compressed_sdch
, feed_block_size
,
1075 output_block_size
, filter
.get(), &output
));
1076 EXPECT_EQ(output
, expanded_
);
1079 TEST_F(SdchFilterTest
, AcceptGzipGzipSdchIfGzip
) {
1080 // Construct a valid SDCH dictionary from a VCDIFF dictionary.
1081 const std::string kSampleDomain
= "sdchtest.com";
1082 std::string
dictionary(NewSdchDictionary(kSampleDomain
));
1084 std::string url_string
= "http://" + kSampleDomain
;
1086 GURL
url(url_string
);
1087 EXPECT_TRUE(sdch_manager_
->AddSdchDictionary(dictionary
, url
));
1089 std::string
sdch_compressed(NewSdchCompressedData(dictionary
));
1091 // Vodaphone (UK) Mobile Broadband provides double gzipped sdch with a content
1092 // encoding of merely gzip (apparently, only listing the extra level of
1093 // wrapper compression they added, but discarding the actual content encoding.
1094 // Use Gzip to double compress the sdch sdch_compressed data.
1095 std::string double_gzip_compressed_sdch
= gzip_compress(gzip_compress(
1098 // Only claim to have gzip content, but really use the double gzipped sdch
1100 // System should automatically add the missing (optional) sdch, gzip decoders.
1101 std::vector
<Filter::FilterType
> filter_types
;
1102 filter_types
.push_back(Filter::FILTER_TYPE_GZIP
);
1104 MockFilterContext filter_context
;
1105 filter_context
.SetMimeType("anything/mime");
1106 filter_context
.SetSdchResponse(true);
1107 Filter::FixupEncodingTypes(filter_context
, &filter_types
);
1108 ASSERT_EQ(filter_types
.size(), 3u);
1109 EXPECT_EQ(filter_types
[0], Filter::FILTER_TYPE_SDCH_POSSIBLE
);
1110 EXPECT_EQ(filter_types
[1], Filter::FILTER_TYPE_GZIP_HELPING_SDCH
);
1111 EXPECT_EQ(filter_types
[2], Filter::FILTER_TYPE_GZIP
);
1113 // First try with a large buffer (larger than test input, or compressed data).
1114 filter_context
.SetURL(url
);
1115 scoped_ptr
<Filter
> filter(Filter::Factory(filter_types
, filter_context
));
1117 // Verify that chained filter is waiting for data.
1118 char tiny_output_buffer
[10];
1119 int tiny_output_size
= sizeof(tiny_output_buffer
);
1120 EXPECT_EQ(Filter::FILTER_NEED_MORE_DATA
,
1121 filter
->ReadData(tiny_output_buffer
, &tiny_output_size
));
1123 size_t feed_block_size
= 100;
1124 size_t output_block_size
= 100;
1126 EXPECT_TRUE(FilterTestData(double_gzip_compressed_sdch
, feed_block_size
,
1127 output_block_size
, filter
.get(), &output
));
1128 EXPECT_EQ(output
, expanded_
);
1130 // Next try with a tiny buffer to cover edge effects.
1131 filter
.reset(Filter::Factory(filter_types
, filter_context
));
1133 feed_block_size
= 1;
1134 output_block_size
= 1;
1136 EXPECT_TRUE(FilterTestData(double_gzip_compressed_sdch
, feed_block_size
,
1137 output_block_size
, filter
.get(), &output
));
1138 EXPECT_EQ(output
, expanded_
);