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_context.h"
17 #include "net/url_request/url_request_http_job.h"
18 #include "testing/gtest/include/gtest/gtest.h"
19 #include "third_party/zlib/zlib.h"
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";
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";
48 //------------------------------------------------------------------------------
50 class SdchFilterTest
: public testing::Test
{
53 : test_vcdiff_dictionary_(kTestVcdiffDictionary
,
54 sizeof(kTestVcdiffDictionary
) - 1),
55 vcdiff_compressed_data_(kSdchCompressedTestData
,
56 sizeof(kSdchCompressedTestData
) - 1),
57 expanded_(kTestData
, sizeof(kTestData
) - 1),
58 sdch_manager_(new SdchManager
),
59 filter_context_(new MockFilterContext
) {
60 URLRequestContext
* url_request_context
=
61 filter_context_
->GetModifiableURLRequestContext();
63 url_request_context
->set_sdch_manager(sdch_manager_
.get());
66 // Attempt to add a dictionary to the manager; returns whether or not
67 // the attempt succeeded.
68 bool AddSdchDictionary(const std::string
& dictionary_text
,
70 return sdch_manager_
->AddSdchDictionary(dictionary_text
, gurl
) == SDCH_OK
;
73 MockFilterContext
* filter_context() { return filter_context_
.get(); }
75 std::string
NewSdchCompressedData(const std::string dictionary
) {
76 std::string client_hash
;
77 std::string server_hash
;
78 SdchManager::GenerateHash(dictionary
, &client_hash
, &server_hash
);
80 // Build compressed data that refers to our dictionary.
81 std::string
compressed(server_hash
);
82 compressed
.append("\0", 1);
83 compressed
.append(vcdiff_compressed_data_
);
87 const std::string test_vcdiff_dictionary_
;
88 const std::string vcdiff_compressed_data_
;
89 const std::string expanded_
; // Desired final, decompressed data.
91 scoped_ptr
<SdchManager
> sdch_manager_
;
92 scoped_ptr
<MockFilterContext
> filter_context_
;
95 //------------------------------------------------------------------------------
98 TEST_F(SdchFilterTest
, Hashing
) {
99 std::string client_hash
, server_hash
;
100 std::string
dictionary("test contents");
101 SdchManager::GenerateHash(dictionary
, &client_hash
, &server_hash
);
103 EXPECT_EQ(client_hash
, "lMQBjS3P");
104 EXPECT_EQ(server_hash
, "MyciMVll");
108 //------------------------------------------------------------------------------
109 // Provide a generic helper function for trying to filter data.
110 // This function repeatedly calls the filter to process data, until the entire
111 // source is consumed. The return value from the filter is appended to output.
112 // This allows us to vary input and output block sizes in order to test for edge
113 // effects (boundary effects?) during the filtering process.
114 // This function provides data to the filter in blocks of no-more-than the
115 // specified input_block_length. It allows the filter to fill no more than
116 // output_buffer_length in any one call to proccess (a.k.a., Read) data, and
117 // concatenates all these little output blocks into the singular output string.
118 static bool FilterTestData(const std::string
& source
,
119 size_t input_block_length
,
120 const size_t output_buffer_length
,
121 Filter
* filter
, std::string
* output
) {
122 CHECK_GT(input_block_length
, 0u);
123 Filter::FilterStatus
status(Filter::FILTER_NEED_MORE_DATA
);
124 size_t source_index
= 0;
125 scoped_ptr
<char[]> output_buffer(new char[output_buffer_length
]);
126 size_t input_amount
= std::min(input_block_length
,
127 static_cast<size_t>(filter
->stream_buffer_size()));
130 int copy_amount
= std::min(input_amount
, source
.size() - source_index
);
131 if (copy_amount
> 0 && status
== Filter::FILTER_NEED_MORE_DATA
) {
132 memcpy(filter
->stream_buffer()->data(), source
.data() + source_index
,
134 filter
->FlushStreamBuffer(copy_amount
);
135 source_index
+= copy_amount
;
137 int buffer_length
= output_buffer_length
;
138 status
= filter
->ReadData(output_buffer
.get(), &buffer_length
);
139 output
->append(output_buffer
.get(), buffer_length
);
140 if (status
== Filter::FILTER_ERROR
)
142 // Callers assume that FILTER_OK with no output buffer means FILTER_DONE.
143 if (Filter::FILTER_OK
== status
&& 0 == buffer_length
)
145 if (copy_amount
== 0 && buffer_length
== 0)
149 //------------------------------------------------------------------------------
150 static std::string
NewSdchDictionary(const std::string
& domain
) {
151 std::string dictionary
;
152 if (!domain
.empty()) {
153 dictionary
.append("Domain: ");
154 dictionary
.append(domain
);
155 dictionary
.append("\n");
157 dictionary
.append("\n");
158 dictionary
.append(kTestVcdiffDictionary
, sizeof(kTestVcdiffDictionary
) - 1);
162 //------------------------------------------------------------------------------
164 TEST_F(SdchFilterTest
, EmptyInputOk
) {
165 std::vector
<Filter::FilterType
> filter_types
;
166 filter_types
.push_back(Filter::FILTER_TYPE_SDCH
);
167 char output_buffer
[20];
168 std::string
url_string("http://ignore.com");
169 filter_context()->SetURL(GURL(url_string
));
170 scoped_ptr
<Filter
> filter(Filter::Factory(filter_types
, *filter_context()));
172 // With no input data, try to read output.
173 int output_bytes_or_buffer_size
= sizeof(output_buffer
);
174 Filter::FilterStatus status
= filter
->ReadData(output_buffer
,
175 &output_bytes_or_buffer_size
);
177 EXPECT_EQ(0, output_bytes_or_buffer_size
);
178 EXPECT_EQ(Filter::FILTER_NEED_MORE_DATA
, status
);
181 // Make sure that the filter context has everything that might be
182 // nuked from it during URLRequest teardown before the SdchFilter
184 TEST_F(SdchFilterTest
, SparseContextOk
) {
185 std::vector
<Filter::FilterType
> filter_types
;
186 filter_types
.push_back(Filter::FILTER_TYPE_SDCH
);
187 char output_buffer
[20];
188 std::string
url_string("http://ignore.com");
189 filter_context()->SetURL(GURL(url_string
));
190 scoped_ptr
<Filter
> filter(Filter::Factory(filter_types
, *filter_context()));
192 // With no input data, try to read output.
193 int output_bytes_or_buffer_size
= sizeof(output_buffer
);
194 Filter::FilterStatus status
= filter
->ReadData(output_buffer
,
195 &output_bytes_or_buffer_size
);
197 EXPECT_EQ(0, output_bytes_or_buffer_size
);
198 EXPECT_EQ(Filter::FILTER_NEED_MORE_DATA
, status
);
200 // Partially tear down context. Anything that goes through request()
201 // without checking it for null in the URLRequestJob::HttpFilterContext
202 // implementation is suspect. Everything that does check it for null should
203 // return null. This is to test for incorrectly relying on filter_context()
204 // from the SdchFilter destructor.
205 filter_context()->NukeUnstableInterfaces();
208 TEST_F(SdchFilterTest
, PassThroughWhenTentative
) {
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_GZIP_HELPING_SDCH
);
212 char output_buffer
[20];
213 // Response code needs to be 200 to allow a pass through.
214 filter_context()->SetResponseCode(200);
215 std::string
url_string("http://ignore.com");
216 filter_context()->SetURL(GURL(url_string
));
217 scoped_ptr
<Filter
> filter(Filter::Factory(filter_types
, *filter_context()));
219 // Supply enough data to force a pass-through mode..
220 std::string
non_gzip_content("not GZIPed data");
222 char* input_buffer
= filter
->stream_buffer()->data();
223 int input_buffer_size
= filter
->stream_buffer_size();
225 EXPECT_LT(static_cast<int>(non_gzip_content
.size()),
227 memcpy(input_buffer
, non_gzip_content
.data(),
228 non_gzip_content
.size());
229 filter
->FlushStreamBuffer(non_gzip_content
.size());
231 // Try to read output.
232 int output_bytes_or_buffer_size
= sizeof(output_buffer
);
233 Filter::FilterStatus status
= filter
->ReadData(output_buffer
,
234 &output_bytes_or_buffer_size
);
236 EXPECT_EQ(non_gzip_content
.size(),
237 static_cast<size_t>(output_bytes_or_buffer_size
));
238 ASSERT_GT(sizeof(output_buffer
),
239 static_cast<size_t>(output_bytes_or_buffer_size
));
240 output_buffer
[output_bytes_or_buffer_size
] = '\0';
241 EXPECT_TRUE(non_gzip_content
== output_buffer
);
242 EXPECT_EQ(Filter::FILTER_NEED_MORE_DATA
, status
);
245 TEST_F(SdchFilterTest
, RefreshBadReturnCode
) {
246 std::vector
<Filter::FilterType
> filter_types
;
247 // Selective a tentative filter (which can fall back to pass through).
248 filter_types
.push_back(Filter::FILTER_TYPE_SDCH_POSSIBLE
);
249 char output_buffer
[20];
250 // Response code needs to be 200 to allow a pass through.
251 filter_context()->SetResponseCode(403);
252 // Meta refresh will only appear for html content
253 filter_context()->SetMimeType("text/html");
254 std::string
url_string("http://ignore.com");
255 filter_context()->SetURL(GURL(url_string
));
256 scoped_ptr
<Filter
> filter(Filter::Factory(filter_types
, *filter_context()));
258 // Supply enough data to force a pass-through mode, which means we have
259 // provided more than 9 characters that can't be a dictionary hash.
260 std::string
non_sdch_content("This is not SDCH");
262 char* input_buffer
= filter
->stream_buffer()->data();
263 int input_buffer_size
= filter
->stream_buffer_size();
265 EXPECT_LT(static_cast<int>(non_sdch_content
.size()),
267 memcpy(input_buffer
, non_sdch_content
.data(),
268 non_sdch_content
.size());
269 filter
->FlushStreamBuffer(non_sdch_content
.size());
271 // Try to read output.
272 int output_bytes_or_buffer_size
= sizeof(output_buffer
);
273 Filter::FilterStatus status
= filter
->ReadData(output_buffer
,
274 &output_bytes_or_buffer_size
);
276 // We should have read a long and complicated meta-refresh request.
277 EXPECT_TRUE(sizeof(output_buffer
) == output_bytes_or_buffer_size
);
278 // Check at least the prefix of the return.
279 EXPECT_EQ(0, strncmp(output_buffer
,
280 "<head><META HTTP-EQUIV=\"Refresh\" CONTENT=\"0\"></head>",
281 sizeof(output_buffer
)));
282 EXPECT_EQ(Filter::FILTER_OK
, status
);
285 TEST_F(SdchFilterTest
, ErrorOnBadReturnCode
) {
286 std::vector
<Filter::FilterType
> filter_types
;
287 // Selective a tentative filter (which can fall back to pass through).
288 filter_types
.push_back(Filter::FILTER_TYPE_SDCH_POSSIBLE
);
289 char output_buffer
[20];
290 // Response code needs to be 200 to allow a pass through.
291 filter_context()->SetResponseCode(403);
292 // Meta refresh will only appear for html content, so set to something else
293 // to induce an error (we can't meta refresh).
294 filter_context()->SetMimeType("anything");
295 std::string
url_string("http://ignore.com");
296 filter_context()->SetURL(GURL(url_string
));
297 scoped_ptr
<Filter
> filter(Filter::Factory(filter_types
, *filter_context()));
299 // Supply enough data to force a pass-through mode, which means we have
300 // provided more than 9 characters that can't be a dictionary hash.
301 std::string
non_sdch_content("This is not SDCH");
303 char* input_buffer
= filter
->stream_buffer()->data();
304 int input_buffer_size
= filter
->stream_buffer_size();
306 EXPECT_LT(static_cast<int>(non_sdch_content
.size()),
308 memcpy(input_buffer
, non_sdch_content
.data(),
309 non_sdch_content
.size());
310 filter
->FlushStreamBuffer(non_sdch_content
.size());
312 // Try to read output.
313 int output_bytes_or_buffer_size
= sizeof(output_buffer
);
314 Filter::FilterStatus status
= filter
->ReadData(output_buffer
,
315 &output_bytes_or_buffer_size
);
317 EXPECT_EQ(0, output_bytes_or_buffer_size
);
318 EXPECT_EQ(Filter::FILTER_ERROR
, status
);
321 TEST_F(SdchFilterTest
, ErrorOnBadReturnCodeWithHtml
) {
322 std::vector
<Filter::FilterType
> filter_types
;
323 // Selective a tentative filter (which can fall back to pass through).
324 filter_types
.push_back(Filter::FILTER_TYPE_SDCH_POSSIBLE
);
325 char output_buffer
[20];
326 // Response code needs to be 200 to allow a pass through.
327 filter_context()->SetResponseCode(403);
328 // Meta refresh will only appear for html content
329 filter_context()->SetMimeType("text/html");
330 std::string
url_string("http://ignore.com");
331 filter_context()->SetURL(GURL(url_string
));
332 scoped_ptr
<Filter
> filter(Filter::Factory(filter_types
, *filter_context()));
334 // Supply enough data to force a pass-through mode, which means we have
335 // provided more than 9 characters that can't be a dictionary hash.
336 std::string
non_sdch_content("This is not SDCH");
338 char* input_buffer
= filter
->stream_buffer()->data();
339 int input_buffer_size
= filter
->stream_buffer_size();
341 EXPECT_LT(static_cast<int>(non_sdch_content
.size()),
343 memcpy(input_buffer
, non_sdch_content
.data(),
344 non_sdch_content
.size());
345 filter
->FlushStreamBuffer(non_sdch_content
.size());
347 // Try to read output.
348 int output_bytes_or_buffer_size
= sizeof(output_buffer
);
349 Filter::FilterStatus status
= filter
->ReadData(output_buffer
,
350 &output_bytes_or_buffer_size
);
352 // We should have read a long and complicated meta-refresh request.
353 EXPECT_EQ(sizeof(output_buffer
),
354 static_cast<size_t>(output_bytes_or_buffer_size
));
355 // Check at least the prefix of the return.
356 EXPECT_EQ(0, strncmp(output_buffer
,
357 "<head><META HTTP-EQUIV=\"Refresh\" CONTENT=\"0\"></head>",
358 sizeof(output_buffer
)));
359 EXPECT_EQ(Filter::FILTER_OK
, status
);
362 TEST_F(SdchFilterTest
, BasicBadDictionary
) {
363 std::vector
<Filter::FilterType
> filter_types
;
364 filter_types
.push_back(Filter::FILTER_TYPE_SDCH
);
365 char output_buffer
[20];
366 std::string
url_string("http://ignore.com");
367 filter_context()->SetURL(GURL(url_string
));
368 scoped_ptr
<Filter
> filter(Filter::Factory(filter_types
, *filter_context()));
370 // Supply bogus data (which doesn't yet specify a full dictionary hash).
371 // Dictionary hash is 8 characters followed by a null.
372 std::string
dictionary_hash_prefix("123");
374 char* input_buffer
= filter
->stream_buffer()->data();
375 int input_buffer_size
= filter
->stream_buffer_size();
377 EXPECT_LT(static_cast<int>(dictionary_hash_prefix
.size()),
379 memcpy(input_buffer
, dictionary_hash_prefix
.data(),
380 dictionary_hash_prefix
.size());
381 filter
->FlushStreamBuffer(dictionary_hash_prefix
.size());
383 // With less than a dictionary specifier, try to read output.
384 int output_bytes_or_buffer_size
= sizeof(output_buffer
);
385 Filter::FilterStatus status
= filter
->ReadData(output_buffer
,
386 &output_bytes_or_buffer_size
);
388 EXPECT_EQ(0, output_bytes_or_buffer_size
);
389 EXPECT_EQ(Filter::FILTER_NEED_MORE_DATA
, status
);
391 // Provide enough data to complete *a* hash, but it is bogus, and not in our
392 // list of dictionaries, so the filter should error out immediately.
393 std::string
dictionary_hash_postfix("4abcd\0", 6);
395 CHECK_LT(dictionary_hash_postfix
.size(),
396 static_cast<size_t>(input_buffer_size
));
397 memcpy(input_buffer
, dictionary_hash_postfix
.data(),
398 dictionary_hash_postfix
.size());
399 filter
->FlushStreamBuffer(dictionary_hash_postfix
.size());
401 // With a non-existant dictionary specifier, try to read output.
402 output_bytes_or_buffer_size
= sizeof(output_buffer
);
403 status
= filter
->ReadData(output_buffer
, &output_bytes_or_buffer_size
);
405 EXPECT_EQ(0, output_bytes_or_buffer_size
);
406 EXPECT_EQ(Filter::FILTER_ERROR
, status
);
408 EXPECT_EQ(SDCH_DOMAIN_BLACKLIST_INCLUDES_TARGET
,
409 sdch_manager_
->IsInSupportedDomain(GURL(url_string
)));
410 sdch_manager_
->ClearBlacklistings();
411 EXPECT_EQ(SDCH_OK
, sdch_manager_
->IsInSupportedDomain(GURL(url_string
)));
414 TEST_F(SdchFilterTest
, DictionaryAddOnce
) {
415 // Construct a valid SDCH dictionary from a VCDIFF dictionary.
416 const std::string kSampleDomain
= "sdchtest.com";
417 std::string
dictionary(NewSdchDictionary(kSampleDomain
));
419 std::string url_string
= "http://" + kSampleDomain
;
420 GURL
url(url_string
);
421 EXPECT_TRUE(AddSdchDictionary(dictionary
, url
));
423 // Check we can't add it twice.
424 EXPECT_FALSE(AddSdchDictionary(dictionary
, url
));
426 const std::string kSampleDomain2
= "sdchtest2.com";
428 // Don't test adding a second dictionary if our limits are tight.
429 if (SdchManager::kMaxDictionaryCount
> 1) {
430 // Construct a second SDCH dictionary from a VCDIFF dictionary.
431 std::string
dictionary2(NewSdchDictionary(kSampleDomain2
));
433 std::string url_string2
= "http://" + kSampleDomain2
;
434 GURL
url2(url_string2
);
435 EXPECT_TRUE(AddSdchDictionary(dictionary2
, url2
));
439 TEST_F(SdchFilterTest
, BasicDictionary
) {
440 // Construct a valid SDCH dictionary from a VCDIFF dictionary.
441 const std::string kSampleDomain
= "sdchtest.com";
442 std::string
dictionary(NewSdchDictionary(kSampleDomain
));
444 std::string url_string
= "http://" + kSampleDomain
;
446 GURL
url(url_string
);
447 EXPECT_TRUE(AddSdchDictionary(dictionary
, url
));
449 std::string
compressed(NewSdchCompressedData(dictionary
));
451 std::vector
<Filter::FilterType
> filter_types
;
452 filter_types
.push_back(Filter::FILTER_TYPE_SDCH
);
454 filter_context()->SetURL(url
);
456 scoped_ptr
<Filter
> filter(Filter::Factory(filter_types
, *filter_context()));
458 size_t feed_block_size
= 100;
459 size_t output_block_size
= 100;
461 EXPECT_TRUE(FilterTestData(compressed
, feed_block_size
, output_block_size
,
462 filter
.get(), &output
));
463 EXPECT_EQ(output
, expanded_
);
465 // Decode with really small buffers (size 1) to check for edge effects.
466 filter
.reset(Filter::Factory(filter_types
, *filter_context()));
469 output_block_size
= 1;
471 EXPECT_TRUE(FilterTestData(compressed
, feed_block_size
, output_block_size
,
472 filter
.get(), &output
));
473 EXPECT_EQ(output
, expanded_
);
476 TEST_F(SdchFilterTest
, NoDecodeHttps
) {
477 // Construct a valid SDCH dictionary from a VCDIFF dictionary.
478 const std::string kSampleDomain
= "sdchtest.com";
479 std::string
dictionary(NewSdchDictionary(kSampleDomain
));
481 std::string url_string
= "http://" + kSampleDomain
;
483 GURL
url(url_string
);
484 EXPECT_TRUE(AddSdchDictionary(dictionary
, url
));
486 std::string
compressed(NewSdchCompressedData(dictionary
));
488 std::vector
<Filter::FilterType
> filter_types
;
489 filter_types
.push_back(Filter::FILTER_TYPE_SDCH
);
491 filter_context()->SetURL(GURL("https://" + kSampleDomain
));
492 scoped_ptr
<Filter
> filter(Filter::Factory(filter_types
, *filter_context()));
494 const size_t feed_block_size(100);
495 const size_t output_block_size(100);
498 EXPECT_FALSE(FilterTestData(compressed
, feed_block_size
, output_block_size
,
499 filter
.get(), &output
));
502 // Current failsafe TODO/hack refuses to decode any content that doesn't use
503 // http as the scheme (see use of DICTIONARY_SELECTED_FOR_NON_HTTP).
504 // The following tests this blockage. Note that blacklisting results, so we
505 // we need separate tests for each of these.
506 TEST_F(SdchFilterTest
, NoDecodeFtp
) {
507 // Construct a valid SDCH dictionary from a VCDIFF dictionary.
508 const std::string kSampleDomain
= "sdchtest.com";
509 std::string
dictionary(NewSdchDictionary(kSampleDomain
));
511 std::string url_string
= "http://" + kSampleDomain
;
513 GURL
url(url_string
);
514 EXPECT_TRUE(AddSdchDictionary(dictionary
, url
));
516 std::string
compressed(NewSdchCompressedData(dictionary
));
518 std::vector
<Filter::FilterType
> filter_types
;
519 filter_types
.push_back(Filter::FILTER_TYPE_SDCH
);
521 filter_context()->SetURL(GURL("ftp://" + kSampleDomain
));
522 scoped_ptr
<Filter
> filter(Filter::Factory(filter_types
, *filter_context()));
524 const size_t feed_block_size(100);
525 const size_t output_block_size(100);
528 EXPECT_FALSE(FilterTestData(compressed
, feed_block_size
, output_block_size
,
529 filter
.get(), &output
));
532 TEST_F(SdchFilterTest
, NoDecodeFileColon
) {
533 // Construct a valid SDCH dictionary from a VCDIFF dictionary.
534 const std::string kSampleDomain
= "sdchtest.com";
535 std::string
dictionary(NewSdchDictionary(kSampleDomain
));
537 std::string url_string
= "http://" + kSampleDomain
;
539 GURL
url(url_string
);
540 EXPECT_TRUE(AddSdchDictionary(dictionary
, url
));
542 std::string
compressed(NewSdchCompressedData(dictionary
));
544 std::vector
<Filter::FilterType
> filter_types
;
545 filter_types
.push_back(Filter::FILTER_TYPE_SDCH
);
547 filter_context()->SetURL(GURL("file://" + kSampleDomain
));
548 scoped_ptr
<Filter
> filter(Filter::Factory(filter_types
, *filter_context()));
550 const size_t feed_block_size(100);
551 const size_t output_block_size(100);
554 EXPECT_FALSE(FilterTestData(compressed
, feed_block_size
, output_block_size
,
555 filter
.get(), &output
));
558 TEST_F(SdchFilterTest
, NoDecodeAboutColon
) {
559 // Construct a valid SDCH dictionary from a VCDIFF dictionary.
560 const std::string kSampleDomain
= "sdchtest.com";
561 std::string
dictionary(NewSdchDictionary(kSampleDomain
));
563 std::string url_string
= "http://" + kSampleDomain
;
565 GURL
url(url_string
);
566 EXPECT_TRUE(AddSdchDictionary(dictionary
, url
));
568 std::string
compressed(NewSdchCompressedData(dictionary
));
570 std::vector
<Filter::FilterType
> filter_types
;
571 filter_types
.push_back(Filter::FILTER_TYPE_SDCH
);
573 filter_context()->SetURL(GURL("about://" + kSampleDomain
));
574 scoped_ptr
<Filter
> filter(Filter::Factory(filter_types
, *filter_context()));
576 const size_t feed_block_size(100);
577 const size_t output_block_size(100);
580 EXPECT_FALSE(FilterTestData(compressed
, feed_block_size
, output_block_size
,
581 filter
.get(), &output
));
584 TEST_F(SdchFilterTest
, NoDecodeJavaScript
) {
585 // Construct a valid SDCH dictionary from a VCDIFF dictionary.
586 const std::string kSampleDomain
= "sdchtest.com";
587 std::string
dictionary(NewSdchDictionary(kSampleDomain
));
589 std::string url_string
= "http://" + kSampleDomain
;
591 GURL
url(url_string
);
592 EXPECT_TRUE(AddSdchDictionary(dictionary
, url
));
594 std::string
compressed(NewSdchCompressedData(dictionary
));
596 std::vector
<Filter::FilterType
> filter_types
;
597 filter_types
.push_back(Filter::FILTER_TYPE_SDCH
);
599 filter_context()->SetURL(GURL("javascript://" + kSampleDomain
));
600 scoped_ptr
<Filter
> filter(Filter::Factory(filter_types
, *filter_context()));
602 const size_t feed_block_size(100);
603 const size_t output_block_size(100);
606 EXPECT_FALSE(FilterTestData(compressed
, feed_block_size
, output_block_size
,
607 filter
.get(), &output
));
610 TEST_F(SdchFilterTest
, CanStillDecodeHttp
) {
611 // Construct a valid SDCH dictionary from a VCDIFF dictionary.
612 const std::string kSampleDomain
= "sdchtest.com";
613 std::string
dictionary(NewSdchDictionary(kSampleDomain
));
615 std::string url_string
= "http://" + kSampleDomain
;
617 GURL
url(url_string
);
618 EXPECT_TRUE(AddSdchDictionary(dictionary
, url
));
620 std::string
compressed(NewSdchCompressedData(dictionary
));
622 std::vector
<Filter::FilterType
> filter_types
;
623 filter_types
.push_back(Filter::FILTER_TYPE_SDCH
);
625 filter_context()->SetURL(GURL("http://" + kSampleDomain
));
626 scoped_ptr
<Filter
> filter(Filter::Factory(filter_types
, *filter_context()));
628 const size_t feed_block_size(100);
629 const size_t output_block_size(100);
632 EXPECT_TRUE(FilterTestData(compressed
, feed_block_size
, output_block_size
,
633 filter
.get(), &output
));
636 TEST_F(SdchFilterTest
, CrossDomainDictionaryUse
) {
637 // Construct a valid SDCH dictionary from a VCDIFF dictionary.
638 const std::string kSampleDomain
= "sdchtest.com";
639 std::string
dictionary(NewSdchDictionary(kSampleDomain
));
641 std::string url_string
= "http://" + kSampleDomain
;
643 GURL
url(url_string
);
644 EXPECT_TRUE(AddSdchDictionary(dictionary
, url
));
646 std::string
compressed(NewSdchCompressedData(dictionary
));
648 std::vector
<Filter::FilterType
> filter_types
;
649 filter_types
.push_back(Filter::FILTER_TYPE_SDCH
);
651 // Decode with content arriving from the "wrong" domain.
652 // This tests SdchManager::CanSet().
653 GURL
wrong_domain_url("http://www.wrongdomain.com");
654 filter_context()->SetURL(wrong_domain_url
);
655 scoped_ptr
<Filter
> filter(Filter::Factory(filter_types
, *filter_context()));
657 size_t feed_block_size
= 100;
658 size_t output_block_size
= 100;
660 EXPECT_FALSE(FilterTestData(compressed
, feed_block_size
, output_block_size
,
661 filter
.get(), &output
));
662 EXPECT_EQ(output
.size(), 0u); // No output written.
664 EXPECT_EQ(SDCH_OK
, sdch_manager_
->IsInSupportedDomain(GURL(url_string
)));
665 EXPECT_EQ(SDCH_DOMAIN_BLACKLIST_INCLUDES_TARGET
,
666 sdch_manager_
->IsInSupportedDomain(wrong_domain_url
));
667 sdch_manager_
->ClearBlacklistings();
668 EXPECT_EQ(SDCH_OK
, sdch_manager_
->IsInSupportedDomain(wrong_domain_url
));
671 TEST_F(SdchFilterTest
, DictionaryPathValidation
) {
672 // Can't test path distinction between dictionaries if we aren't allowed
673 // more than one dictionary.
674 if (SdchManager::kMaxDictionaryCount
<= 1)
677 // Construct a valid SDCH dictionary from a VCDIFF dictionary.
678 const std::string kSampleDomain
= "sdchtest.com";
679 std::string
dictionary(NewSdchDictionary(kSampleDomain
));
681 std::string url_string
= "http://" + kSampleDomain
;
683 GURL
url(url_string
);
684 EXPECT_TRUE(AddSdchDictionary(dictionary
, url
));
686 // Create a dictionary with a path restriction, by prefixing dictionary.
687 const std::string
path("/special_path/bin");
688 std::string
dictionary_with_path("Path: " + path
+ "\n");
689 dictionary_with_path
.append(dictionary
);
690 GURL
url2(url_string
+ path
);
691 EXPECT_TRUE(AddSdchDictionary(dictionary_with_path
, url2
));
693 std::string
compressed_for_path(NewSdchCompressedData(dictionary_with_path
));
695 std::vector
<Filter::FilterType
> filter_types
;
696 filter_types
.push_back(Filter::FILTER_TYPE_SDCH
);
698 // Test decode the path data, arriving from a valid path.
699 filter_context()->SetURL(GURL(url_string
+ path
));
700 scoped_ptr
<Filter
> filter(Filter::Factory(filter_types
, *filter_context()));
702 size_t feed_block_size
= 100;
703 size_t output_block_size
= 100;
706 EXPECT_TRUE(FilterTestData(compressed_for_path
, feed_block_size
,
707 output_block_size
, filter
.get(), &output
));
708 EXPECT_EQ(output
, expanded_
);
710 // Test decode the path data, arriving from a invalid path.
711 filter_context()->SetURL(GURL(url_string
));
712 filter
.reset(Filter::Factory(filter_types
, *filter_context()));
714 feed_block_size
= 100;
715 output_block_size
= 100;
717 EXPECT_FALSE(FilterTestData(compressed_for_path
, feed_block_size
,
718 output_block_size
, filter
.get(), &output
));
719 EXPECT_EQ(output
.size(), 0u); // No output written.
721 EXPECT_EQ(SDCH_DOMAIN_BLACKLIST_INCLUDES_TARGET
,
722 sdch_manager_
->IsInSupportedDomain(GURL(url_string
)));
723 sdch_manager_
->ClearBlacklistings();
724 EXPECT_EQ(SDCH_OK
, sdch_manager_
->IsInSupportedDomain(GURL(url_string
)));
727 TEST_F(SdchFilterTest
, DictionaryPortValidation
) {
728 // Can't test port distinction between dictionaries if we aren't allowed
729 // more than one dictionary.
730 if (SdchManager::kMaxDictionaryCount
<= 1)
733 // Construct a valid SDCH dictionary from a VCDIFF dictionary.
734 const std::string kSampleDomain
= "sdchtest.com";
735 std::string
dictionary(NewSdchDictionary(kSampleDomain
));
737 std::string url_string
= "http://" + kSampleDomain
;
739 GURL
url(url_string
);
740 EXPECT_TRUE(AddSdchDictionary(dictionary
, url
));
742 // Create a dictionary with a port restriction, by prefixing old dictionary.
743 const std::string
port("502");
744 std::string
dictionary_with_port("Port: " + port
+ "\n");
745 dictionary_with_port
.append("Port: 80\n"); // Add default port.
746 dictionary_with_port
.append(dictionary
);
747 EXPECT_TRUE(AddSdchDictionary(dictionary_with_port
,
748 GURL(url_string
+ ":" + port
)));
750 std::string
compressed_for_port(NewSdchCompressedData(dictionary_with_port
));
752 std::vector
<Filter::FilterType
> filter_types
;
753 filter_types
.push_back(Filter::FILTER_TYPE_SDCH
);
755 // Test decode the port data, arriving from a valid port.
756 filter_context()->SetURL(GURL(url_string
+ ":" + port
));
757 scoped_ptr
<Filter
> filter(Filter::Factory(filter_types
, *filter_context()));
759 size_t feed_block_size
= 100;
760 size_t output_block_size
= 100;
762 EXPECT_TRUE(FilterTestData(compressed_for_port
, feed_block_size
,
763 output_block_size
, filter
.get(), &output
));
764 EXPECT_EQ(output
, expanded_
);
766 // Test decode the port data, arriving from a valid (default) port.
767 filter_context()->SetURL(GURL(url_string
)); // Default port.
768 filter
.reset(Filter::Factory(filter_types
, *filter_context()));
770 feed_block_size
= 100;
771 output_block_size
= 100;
773 EXPECT_TRUE(FilterTestData(compressed_for_port
, feed_block_size
,
774 output_block_size
, filter
.get(), &output
));
775 EXPECT_EQ(output
, expanded_
);
777 // Test decode the port data, arriving from a invalid port.
778 filter_context()->SetURL(GURL(url_string
+ ":" + port
+ "1"));
779 filter
.reset(Filter::Factory(filter_types
, *filter_context()));
781 feed_block_size
= 100;
782 output_block_size
= 100;
784 EXPECT_FALSE(FilterTestData(compressed_for_port
, feed_block_size
,
785 output_block_size
, filter
.get(), &output
));
786 EXPECT_EQ(output
.size(), 0u); // No output written.
788 EXPECT_EQ(SDCH_DOMAIN_BLACKLIST_INCLUDES_TARGET
,
789 sdch_manager_
->IsInSupportedDomain(GURL(url_string
)));
790 sdch_manager_
->ClearBlacklistings();
791 EXPECT_EQ(SDCH_OK
, sdch_manager_
->IsInSupportedDomain(GURL(url_string
)));
794 //------------------------------------------------------------------------------
795 // Helper function to perform gzip compression of data.
797 static std::string
gzip_compress(const std::string
&input
) {
798 z_stream zlib_stream
;
799 memset(&zlib_stream
, 0, sizeof(zlib_stream
));
803 code
= deflateInit2(&zlib_stream
, Z_DEFAULT_COMPRESSION
, Z_DEFLATED
,
808 CHECK_EQ(Z_OK
, code
);
810 // Fill in zlib control block
811 zlib_stream
.next_in
= bit_cast
<Bytef
*>(input
.data());
812 zlib_stream
.avail_in
= input
.size();
814 // Assume we can compress into similar buffer (add 100 bytes to be sure).
815 size_t gzip_compressed_length
= zlib_stream
.avail_in
+ 100;
816 scoped_ptr
<char[]> gzip_compressed(new char[gzip_compressed_length
]);
817 zlib_stream
.next_out
= bit_cast
<Bytef
*>(gzip_compressed
.get());
818 zlib_stream
.avail_out
= gzip_compressed_length
;
820 // The GZIP header (see RFC 1952):
821 // +---+---+---+---+---+---+---+---+---+---+
822 // |ID1|ID2|CM |FLG| MTIME |XFL|OS |
823 // +---+---+---+---+---+---+---+---+---+---+
826 // CM \010 (compression method == DEFLATE)
827 // FLG \000 (special flags that we do not support)
828 // MTIME Unix format modification time (0 means not available)
829 // XFL 2-4? DEFLATE flags
830 // OS ???? Operating system indicator (255 means unknown)
832 // Header value we generate:
833 const char kGZipHeader
[] = { '\037', '\213', '\010', '\000', '\000',
834 '\000', '\000', '\000', '\002', '\377' };
835 CHECK_GT(zlib_stream
.avail_out
, sizeof(kGZipHeader
));
836 memcpy(zlib_stream
.next_out
, kGZipHeader
, sizeof(kGZipHeader
));
837 zlib_stream
.next_out
+= sizeof(kGZipHeader
);
838 zlib_stream
.avail_out
-= sizeof(kGZipHeader
);
841 code
= deflate(&zlib_stream
, Z_FINISH
);
842 gzip_compressed_length
-= zlib_stream
.avail_out
;
843 std::string
compressed(gzip_compressed
.get(), gzip_compressed_length
);
844 deflateEnd(&zlib_stream
);
848 //------------------------------------------------------------------------------
850 class SdchFilterChainingTest
{
852 static Filter
* Factory(const std::vector
<Filter::FilterType
>& types
,
853 const FilterContext
& context
, int size
) {
854 return Filter::FactoryForTests(types
, context
, size
);
858 // Test that filters can be cascaded (chained) so that the output of one filter
859 // is processed by the next one. This is most critical for SDCH, which is
860 // routinely followed by gzip (during encoding). The filter we'll test for will
861 // do the gzip decoding first, and then decode the SDCH content.
862 TEST_F(SdchFilterTest
, FilterChaining
) {
863 // Construct a valid SDCH dictionary from a VCDIFF dictionary.
864 const std::string kSampleDomain
= "sdchtest.com";
865 std::string
dictionary(NewSdchDictionary(kSampleDomain
));
867 std::string url_string
= "http://" + kSampleDomain
;
869 GURL
url(url_string
);
870 EXPECT_TRUE(AddSdchDictionary(dictionary
, url
));
872 std::string
sdch_compressed(NewSdchCompressedData(dictionary
));
874 // Use Gzip to compress the sdch sdch_compressed data.
875 std::string gzip_compressed_sdch
= gzip_compress(sdch_compressed
);
877 // Construct a chained filter.
878 std::vector
<Filter::FilterType
> filter_types
;
879 filter_types
.push_back(Filter::FILTER_TYPE_SDCH
);
880 filter_types
.push_back(Filter::FILTER_TYPE_GZIP
);
882 // First try with a large buffer (larger than test input, or compressed data).
883 const size_t kLargeInputBufferSize(1000); // Used internally in filters.
884 CHECK_GT(kLargeInputBufferSize
, gzip_compressed_sdch
.size());
885 CHECK_GT(kLargeInputBufferSize
, sdch_compressed
.size());
886 CHECK_GT(kLargeInputBufferSize
, expanded_
.size());
887 filter_context()->SetURL(url
);
888 scoped_ptr
<Filter
> filter(
889 SdchFilterChainingTest::Factory(filter_types
, *filter_context(),
890 kLargeInputBufferSize
));
891 EXPECT_EQ(static_cast<int>(kLargeInputBufferSize
),
892 filter
->stream_buffer_size());
894 // Verify that chained filter is waiting for data.
895 char tiny_output_buffer
[10];
896 int tiny_output_size
= sizeof(tiny_output_buffer
);
897 EXPECT_EQ(Filter::FILTER_NEED_MORE_DATA
,
898 filter
->ReadData(tiny_output_buffer
, &tiny_output_size
));
900 // Make chain process all data.
901 size_t feed_block_size
= kLargeInputBufferSize
;
902 size_t output_block_size
= kLargeInputBufferSize
;
904 EXPECT_TRUE(FilterTestData(gzip_compressed_sdch
, feed_block_size
,
905 output_block_size
, filter
.get(), &output
));
906 EXPECT_EQ(output
, expanded_
);
908 // Next try with a mid-sized internal buffer size.
909 const size_t kMidSizedInputBufferSize(100);
910 // Buffer should be big enough to swallow whole gzip content.
911 CHECK_GT(kMidSizedInputBufferSize
, gzip_compressed_sdch
.size());
912 // Buffer should be small enough that entire SDCH content can't fit.
913 // We'll go even further, and force the chain to flush the buffer between the
914 // two filters more than once (that is why we multiply by 2).
915 CHECK_LT(kMidSizedInputBufferSize
* 2, sdch_compressed
.size());
916 filter_context()->SetURL(url
);
918 SdchFilterChainingTest::Factory(filter_types
, *filter_context(),
919 kMidSizedInputBufferSize
));
920 EXPECT_EQ(static_cast<int>(kMidSizedInputBufferSize
),
921 filter
->stream_buffer_size());
923 feed_block_size
= kMidSizedInputBufferSize
;
924 output_block_size
= kMidSizedInputBufferSize
;
926 EXPECT_TRUE(FilterTestData(gzip_compressed_sdch
, feed_block_size
,
927 output_block_size
, filter
.get(), &output
));
928 EXPECT_EQ(output
, expanded_
);
930 // Next try with a tiny input and output buffer to cover edge effects.
931 filter
.reset(SdchFilterChainingTest::Factory(filter_types
, *filter_context(),
932 kLargeInputBufferSize
));
933 EXPECT_EQ(static_cast<int>(kLargeInputBufferSize
),
934 filter
->stream_buffer_size());
937 output_block_size
= 1;
939 EXPECT_TRUE(FilterTestData(gzip_compressed_sdch
, feed_block_size
,
940 output_block_size
, filter
.get(), &output
));
941 EXPECT_EQ(output
, expanded_
);
944 TEST_F(SdchFilterTest
, DefaultGzipIfSdch
) {
945 // Construct a valid SDCH dictionary from a VCDIFF dictionary.
946 const std::string kSampleDomain
= "sdchtest.com";
947 std::string
dictionary(NewSdchDictionary(kSampleDomain
));
949 std::string url_string
= "http://" + kSampleDomain
;
951 GURL
url(url_string
);
952 EXPECT_TRUE(AddSdchDictionary(dictionary
, url
));
954 std::string
sdch_compressed(NewSdchCompressedData(dictionary
));
956 // Use Gzip to compress the sdch sdch_compressed data.
957 std::string gzip_compressed_sdch
= gzip_compress(sdch_compressed
);
959 // Only claim to have sdch content, but really use the gzipped sdch content.
960 // System should automatically add the missing (optional) gzip.
961 std::vector
<Filter::FilterType
> filter_types
;
962 filter_types
.push_back(Filter::FILTER_TYPE_SDCH
);
964 filter_context()->SetMimeType("anything/mime");
965 filter_context()->SetSdchResponse(true);
966 Filter::FixupEncodingTypes(*filter_context(), &filter_types
);
967 ASSERT_EQ(filter_types
.size(), 2u);
968 EXPECT_EQ(filter_types
[0], Filter::FILTER_TYPE_SDCH
);
969 EXPECT_EQ(filter_types
[1], Filter::FILTER_TYPE_GZIP_HELPING_SDCH
);
971 // First try with a large buffer (larger than test input, or compressed data).
972 filter_context()->SetURL(url
);
973 scoped_ptr
<Filter
> filter(Filter::Factory(filter_types
, *filter_context()));
976 // Verify that chained filter is waiting for data.
977 char tiny_output_buffer
[10];
978 int tiny_output_size
= sizeof(tiny_output_buffer
);
979 EXPECT_EQ(Filter::FILTER_NEED_MORE_DATA
,
980 filter
->ReadData(tiny_output_buffer
, &tiny_output_size
));
982 size_t feed_block_size
= 100;
983 size_t output_block_size
= 100;
985 EXPECT_TRUE(FilterTestData(gzip_compressed_sdch
, feed_block_size
,
986 output_block_size
, filter
.get(), &output
));
987 EXPECT_EQ(output
, expanded_
);
989 // Next try with a tiny buffer to cover edge effects.
990 filter
.reset(Filter::Factory(filter_types
, *filter_context()));
993 output_block_size
= 1;
995 EXPECT_TRUE(FilterTestData(gzip_compressed_sdch
, feed_block_size
,
996 output_block_size
, filter
.get(), &output
));
997 EXPECT_EQ(output
, expanded_
);
1000 TEST_F(SdchFilterTest
, AcceptGzipSdchIfGzip
) {
1001 // Construct a valid SDCH dictionary from a VCDIFF dictionary.
1002 const std::string kSampleDomain
= "sdchtest.com";
1003 std::string
dictionary(NewSdchDictionary(kSampleDomain
));
1005 std::string url_string
= "http://" + kSampleDomain
;
1007 GURL
url(url_string
);
1008 EXPECT_TRUE(AddSdchDictionary(dictionary
, url
));
1010 std::string
sdch_compressed(NewSdchCompressedData(dictionary
));
1012 // Use Gzip to compress the sdch sdch_compressed data.
1013 std::string gzip_compressed_sdch
= gzip_compress(sdch_compressed
);
1015 // Some proxies strip the content encoding statement down to a mere gzip, but
1016 // pass through the original content (with full sdch,gzip encoding).
1017 // Only claim to have gzip content, but really use the gzipped sdch content.
1018 // System should automatically add the missing (optional) sdch.
1019 std::vector
<Filter::FilterType
> filter_types
;
1020 filter_types
.push_back(Filter::FILTER_TYPE_GZIP
);
1022 filter_context()->SetMimeType("anything/mime");
1023 filter_context()->SetSdchResponse(true);
1024 Filter::FixupEncodingTypes(*filter_context(), &filter_types
);
1025 ASSERT_EQ(filter_types
.size(), 3u);
1026 EXPECT_EQ(filter_types
[0], Filter::FILTER_TYPE_SDCH_POSSIBLE
);
1027 EXPECT_EQ(filter_types
[1], Filter::FILTER_TYPE_GZIP_HELPING_SDCH
);
1028 EXPECT_EQ(filter_types
[2], Filter::FILTER_TYPE_GZIP
);
1030 // First try with a large buffer (larger than test input, or compressed data).
1031 filter_context()->SetURL(url
);
1032 scoped_ptr
<Filter
> filter(Filter::Factory(filter_types
, *filter_context()));
1035 // Verify that chained filter is waiting for data.
1036 char tiny_output_buffer
[10];
1037 int tiny_output_size
= sizeof(tiny_output_buffer
);
1038 EXPECT_EQ(Filter::FILTER_NEED_MORE_DATA
,
1039 filter
->ReadData(tiny_output_buffer
, &tiny_output_size
));
1041 size_t feed_block_size
= 100;
1042 size_t output_block_size
= 100;
1044 EXPECT_TRUE(FilterTestData(gzip_compressed_sdch
, feed_block_size
,
1045 output_block_size
, filter
.get(), &output
));
1046 EXPECT_EQ(output
, expanded_
);
1048 // Next try with a tiny buffer to cover edge effects.
1049 filter
.reset(Filter::Factory(filter_types
, *filter_context()));
1051 feed_block_size
= 1;
1052 output_block_size
= 1;
1054 EXPECT_TRUE(FilterTestData(gzip_compressed_sdch
, feed_block_size
,
1055 output_block_size
, filter
.get(), &output
));
1056 EXPECT_EQ(output
, expanded_
);
1059 TEST_F(SdchFilterTest
, DefaultSdchGzipIfEmpty
) {
1060 // Construct a valid SDCH dictionary from a VCDIFF dictionary.
1061 const std::string kSampleDomain
= "sdchtest.com";
1062 std::string
dictionary(NewSdchDictionary(kSampleDomain
));
1064 std::string url_string
= "http://" + kSampleDomain
;
1066 GURL
url(url_string
);
1067 EXPECT_TRUE(AddSdchDictionary(dictionary
, url
));
1069 std::string
sdch_compressed(NewSdchCompressedData(dictionary
));
1071 // Use Gzip to compress the sdch sdch_compressed data.
1072 std::string gzip_compressed_sdch
= gzip_compress(sdch_compressed
);
1074 // Only claim to have non-encoded content, but really use the gzipped sdch
1076 // System should automatically add the missing (optional) sdch,gzip.
1077 std::vector
<Filter::FilterType
> filter_types
;
1079 filter_context()->SetMimeType("anything/mime");
1080 filter_context()->SetSdchResponse(true);
1081 Filter::FixupEncodingTypes(*filter_context(), &filter_types
);
1082 ASSERT_EQ(filter_types
.size(), 2u);
1083 EXPECT_EQ(filter_types
[0], Filter::FILTER_TYPE_SDCH_POSSIBLE
);
1084 EXPECT_EQ(filter_types
[1], Filter::FILTER_TYPE_GZIP_HELPING_SDCH
);
1086 // First try with a large buffer (larger than test input, or compressed data).
1087 filter_context()->SetURL(url
);
1088 scoped_ptr
<Filter
> filter(Filter::Factory(filter_types
, *filter_context()));
1091 // Verify that chained filter is waiting for data.
1092 char tiny_output_buffer
[10];
1093 int tiny_output_size
= sizeof(tiny_output_buffer
);
1094 EXPECT_EQ(Filter::FILTER_NEED_MORE_DATA
,
1095 filter
->ReadData(tiny_output_buffer
, &tiny_output_size
));
1097 size_t feed_block_size
= 100;
1098 size_t output_block_size
= 100;
1100 EXPECT_TRUE(FilterTestData(gzip_compressed_sdch
, feed_block_size
,
1101 output_block_size
, filter
.get(), &output
));
1102 EXPECT_EQ(output
, expanded_
);
1104 // Next try with a tiny buffer to cover edge effects.
1105 filter
.reset(Filter::Factory(filter_types
, *filter_context()));
1107 feed_block_size
= 1;
1108 output_block_size
= 1;
1110 EXPECT_TRUE(FilterTestData(gzip_compressed_sdch
, feed_block_size
,
1111 output_block_size
, filter
.get(), &output
));
1112 EXPECT_EQ(output
, expanded_
);
1115 TEST_F(SdchFilterTest
, AcceptGzipGzipSdchIfGzip
) {
1116 // Construct a valid SDCH dictionary from a VCDIFF dictionary.
1117 const std::string kSampleDomain
= "sdchtest.com";
1118 std::string
dictionary(NewSdchDictionary(kSampleDomain
));
1120 std::string url_string
= "http://" + kSampleDomain
;
1122 GURL
url(url_string
);
1123 EXPECT_TRUE(AddSdchDictionary(dictionary
, url
));
1125 std::string
sdch_compressed(NewSdchCompressedData(dictionary
));
1127 // Vodaphone (UK) Mobile Broadband provides double gzipped sdch with a content
1128 // encoding of merely gzip (apparently, only listing the extra level of
1129 // wrapper compression they added, but discarding the actual content encoding.
1130 // Use Gzip to double compress the sdch sdch_compressed data.
1131 std::string double_gzip_compressed_sdch
= gzip_compress(gzip_compress(
1134 // Only claim to have gzip content, but really use the double gzipped sdch
1136 // System should automatically add the missing (optional) sdch, gzip decoders.
1137 std::vector
<Filter::FilterType
> filter_types
;
1138 filter_types
.push_back(Filter::FILTER_TYPE_GZIP
);
1140 filter_context()->SetMimeType("anything/mime");
1141 filter_context()->SetSdchResponse(true);
1142 Filter::FixupEncodingTypes(*filter_context(), &filter_types
);
1143 ASSERT_EQ(filter_types
.size(), 3u);
1144 EXPECT_EQ(filter_types
[0], Filter::FILTER_TYPE_SDCH_POSSIBLE
);
1145 EXPECT_EQ(filter_types
[1], Filter::FILTER_TYPE_GZIP_HELPING_SDCH
);
1146 EXPECT_EQ(filter_types
[2], Filter::FILTER_TYPE_GZIP
);
1148 // First try with a large buffer (larger than test input, or compressed data).
1149 filter_context()->SetURL(url
);
1150 scoped_ptr
<Filter
> filter(Filter::Factory(filter_types
, *filter_context()));
1152 // Verify that chained filter is waiting for data.
1153 char tiny_output_buffer
[10];
1154 int tiny_output_size
= sizeof(tiny_output_buffer
);
1155 EXPECT_EQ(Filter::FILTER_NEED_MORE_DATA
,
1156 filter
->ReadData(tiny_output_buffer
, &tiny_output_size
));
1158 size_t feed_block_size
= 100;
1159 size_t output_block_size
= 100;
1161 EXPECT_TRUE(FilterTestData(double_gzip_compressed_sdch
, feed_block_size
,
1162 output_block_size
, filter
.get(), &output
));
1163 EXPECT_EQ(output
, expanded_
);
1165 // Next try with a tiny buffer to cover edge effects.
1166 filter
.reset(Filter::Factory(filter_types
, *filter_context()));
1168 feed_block_size
= 1;
1169 output_block_size
= 1;
1171 EXPECT_TRUE(FilterTestData(double_gzip_compressed_sdch
, feed_block_size
,
1172 output_block_size
, filter
.get(), &output
));
1173 EXPECT_EQ(output
, expanded_
);