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
,
71 sdch_manager_
->GetAvailDictionaryList(gurl
, &list
);
72 sdch_manager_
->AddSdchDictionary(dictionary_text
, gurl
);
74 sdch_manager_
->GetAvailDictionaryList(gurl
, &list2
);
76 // The list of hashes should change iff the addition succeeds.
77 return (list
!= list2
);
80 MockFilterContext
* filter_context() { return filter_context_
.get(); }
82 std::string
NewSdchCompressedData(const std::string dictionary
) {
83 std::string client_hash
;
84 std::string server_hash
;
85 SdchManager::GenerateHash(dictionary
, &client_hash
, &server_hash
);
87 // Build compressed data that refers to our dictionary.
88 std::string
compressed(server_hash
);
89 compressed
.append("\0", 1);
90 compressed
.append(vcdiff_compressed_data_
);
94 const std::string test_vcdiff_dictionary_
;
95 const std::string vcdiff_compressed_data_
;
96 const std::string expanded_
; // Desired final, decompressed data.
98 scoped_ptr
<SdchManager
> sdch_manager_
;
99 scoped_ptr
<MockFilterContext
> filter_context_
;
102 //------------------------------------------------------------------------------
105 TEST_F(SdchFilterTest
, Hashing
) {
106 std::string client_hash
, server_hash
;
107 std::string
dictionary("test contents");
108 SdchManager::GenerateHash(dictionary
, &client_hash
, &server_hash
);
110 EXPECT_EQ(client_hash
, "lMQBjS3P");
111 EXPECT_EQ(server_hash
, "MyciMVll");
115 //------------------------------------------------------------------------------
116 // Provide a generic helper function for trying to filter data.
117 // This function repeatedly calls the filter to process data, until the entire
118 // source is consumed. The return value from the filter is appended to output.
119 // This allows us to vary input and output block sizes in order to test for edge
120 // effects (boundary effects?) during the filtering process.
121 // This function provides data to the filter in blocks of no-more-than the
122 // specified input_block_length. It allows the filter to fill no more than
123 // output_buffer_length in any one call to proccess (a.k.a., Read) data, and
124 // concatenates all these little output blocks into the singular output string.
125 static bool FilterTestData(const std::string
& source
,
126 size_t input_block_length
,
127 const size_t output_buffer_length
,
128 Filter
* filter
, std::string
* output
) {
129 CHECK_GT(input_block_length
, 0u);
130 Filter::FilterStatus
status(Filter::FILTER_NEED_MORE_DATA
);
131 size_t source_index
= 0;
132 scoped_ptr
<char[]> output_buffer(new char[output_buffer_length
]);
133 size_t input_amount
= std::min(input_block_length
,
134 static_cast<size_t>(filter
->stream_buffer_size()));
137 int copy_amount
= std::min(input_amount
, source
.size() - source_index
);
138 if (copy_amount
> 0 && status
== Filter::FILTER_NEED_MORE_DATA
) {
139 memcpy(filter
->stream_buffer()->data(), source
.data() + source_index
,
141 filter
->FlushStreamBuffer(copy_amount
);
142 source_index
+= copy_amount
;
144 int buffer_length
= output_buffer_length
;
145 status
= filter
->ReadData(output_buffer
.get(), &buffer_length
);
146 output
->append(output_buffer
.get(), buffer_length
);
147 if (status
== Filter::FILTER_ERROR
)
149 // Callers assume that FILTER_OK with no output buffer means FILTER_DONE.
150 if (Filter::FILTER_OK
== status
&& 0 == buffer_length
)
152 if (copy_amount
== 0 && buffer_length
== 0)
156 //------------------------------------------------------------------------------
157 static std::string
NewSdchDictionary(const std::string
& domain
) {
158 std::string dictionary
;
159 if (!domain
.empty()) {
160 dictionary
.append("Domain: ");
161 dictionary
.append(domain
);
162 dictionary
.append("\n");
164 dictionary
.append("\n");
165 dictionary
.append(kTestVcdiffDictionary
, sizeof(kTestVcdiffDictionary
) - 1);
169 //------------------------------------------------------------------------------
171 TEST_F(SdchFilterTest
, EmptyInputOk
) {
172 std::vector
<Filter::FilterType
> filter_types
;
173 filter_types
.push_back(Filter::FILTER_TYPE_SDCH
);
174 char output_buffer
[20];
175 std::string
url_string("http://ignore.com");
176 filter_context()->SetURL(GURL(url_string
));
177 scoped_ptr
<Filter
> filter(Filter::Factory(filter_types
, *filter_context()));
179 // With no input data, try to read output.
180 int output_bytes_or_buffer_size
= sizeof(output_buffer
);
181 Filter::FilterStatus status
= filter
->ReadData(output_buffer
,
182 &output_bytes_or_buffer_size
);
184 EXPECT_EQ(0, output_bytes_or_buffer_size
);
185 EXPECT_EQ(Filter::FILTER_NEED_MORE_DATA
, status
);
188 // Make sure that the filter context has everything that might be
189 // nuked from it during URLRequest teardown before the SdchFilter
191 TEST_F(SdchFilterTest
, SparseContextOk
) {
192 std::vector
<Filter::FilterType
> filter_types
;
193 filter_types
.push_back(Filter::FILTER_TYPE_SDCH
);
194 char output_buffer
[20];
195 std::string
url_string("http://ignore.com");
196 filter_context()->SetURL(GURL(url_string
));
197 scoped_ptr
<Filter
> filter(Filter::Factory(filter_types
, *filter_context()));
199 // With no input data, try to read output.
200 int output_bytes_or_buffer_size
= sizeof(output_buffer
);
201 Filter::FilterStatus status
= filter
->ReadData(output_buffer
,
202 &output_bytes_or_buffer_size
);
204 EXPECT_EQ(0, output_bytes_or_buffer_size
);
205 EXPECT_EQ(Filter::FILTER_NEED_MORE_DATA
, status
);
207 // Partially tear down context. Anything that goes through request()
208 // without checking it for null in the URLRequestJob::HttpFilterContext
209 // implementation is suspect. Everything that does check it for null should
210 // return null. This is to test for incorrectly relying on filter_context()
211 // from the SdchFilter destructor.
212 filter_context()->NukeUnstableInterfaces();
215 TEST_F(SdchFilterTest
, PassThroughWhenTentative
) {
216 std::vector
<Filter::FilterType
> filter_types
;
217 // Selective a tentative filter (which can fall back to pass through).
218 filter_types
.push_back(Filter::FILTER_TYPE_GZIP_HELPING_SDCH
);
219 char output_buffer
[20];
220 // Response code needs to be 200 to allow a pass through.
221 filter_context()->SetResponseCode(200);
222 std::string
url_string("http://ignore.com");
223 filter_context()->SetURL(GURL(url_string
));
224 scoped_ptr
<Filter
> filter(Filter::Factory(filter_types
, *filter_context()));
226 // Supply enough data to force a pass-through mode..
227 std::string
non_gzip_content("not GZIPed data");
229 char* input_buffer
= filter
->stream_buffer()->data();
230 int input_buffer_size
= filter
->stream_buffer_size();
232 EXPECT_LT(static_cast<int>(non_gzip_content
.size()),
234 memcpy(input_buffer
, non_gzip_content
.data(),
235 non_gzip_content
.size());
236 filter
->FlushStreamBuffer(non_gzip_content
.size());
238 // Try to read output.
239 int output_bytes_or_buffer_size
= sizeof(output_buffer
);
240 Filter::FilterStatus status
= filter
->ReadData(output_buffer
,
241 &output_bytes_or_buffer_size
);
243 EXPECT_EQ(non_gzip_content
.size(),
244 static_cast<size_t>(output_bytes_or_buffer_size
));
245 ASSERT_GT(sizeof(output_buffer
),
246 static_cast<size_t>(output_bytes_or_buffer_size
));
247 output_buffer
[output_bytes_or_buffer_size
] = '\0';
248 EXPECT_TRUE(non_gzip_content
== output_buffer
);
249 EXPECT_EQ(Filter::FILTER_NEED_MORE_DATA
, status
);
252 TEST_F(SdchFilterTest
, RefreshBadReturnCode
) {
253 std::vector
<Filter::FilterType
> filter_types
;
254 // Selective a tentative filter (which can fall back to pass through).
255 filter_types
.push_back(Filter::FILTER_TYPE_SDCH_POSSIBLE
);
256 char output_buffer
[20];
257 // Response code needs to be 200 to allow a pass through.
258 filter_context()->SetResponseCode(403);
259 // Meta refresh will only appear for html content
260 filter_context()->SetMimeType("text/html");
261 std::string
url_string("http://ignore.com");
262 filter_context()->SetURL(GURL(url_string
));
263 scoped_ptr
<Filter
> filter(Filter::Factory(filter_types
, *filter_context()));
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");
269 char* input_buffer
= filter
->stream_buffer()->data();
270 int input_buffer_size
= filter
->stream_buffer_size();
272 EXPECT_LT(static_cast<int>(non_sdch_content
.size()),
274 memcpy(input_buffer
, non_sdch_content
.data(),
275 non_sdch_content
.size());
276 filter
->FlushStreamBuffer(non_sdch_content
.size());
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
);
283 // We should have read a long and complicated meta-refresh request.
284 EXPECT_TRUE(sizeof(output_buffer
) == output_bytes_or_buffer_size
);
285 // Check at least the prefix of the return.
286 EXPECT_EQ(0, strncmp(output_buffer
,
287 "<head><META HTTP-EQUIV=\"Refresh\" CONTENT=\"0\"></head>",
288 sizeof(output_buffer
)));
289 EXPECT_EQ(Filter::FILTER_OK
, status
);
292 TEST_F(SdchFilterTest
, ErrorOnBadReturnCode
) {
293 std::vector
<Filter::FilterType
> filter_types
;
294 // Selective a tentative filter (which can fall back to pass through).
295 filter_types
.push_back(Filter::FILTER_TYPE_SDCH_POSSIBLE
);
296 char output_buffer
[20];
297 // Response code needs to be 200 to allow a pass through.
298 filter_context()->SetResponseCode(403);
299 // Meta refresh will only appear for html content, so set to something else
300 // to induce an error (we can't meta refresh).
301 filter_context()->SetMimeType("anything");
302 std::string
url_string("http://ignore.com");
303 filter_context()->SetURL(GURL(url_string
));
304 scoped_ptr
<Filter
> filter(Filter::Factory(filter_types
, *filter_context()));
306 // Supply enough data to force a pass-through mode, which means we have
307 // provided more than 9 characters that can't be a dictionary hash.
308 std::string
non_sdch_content("This is not SDCH");
310 char* input_buffer
= filter
->stream_buffer()->data();
311 int input_buffer_size
= filter
->stream_buffer_size();
313 EXPECT_LT(static_cast<int>(non_sdch_content
.size()),
315 memcpy(input_buffer
, non_sdch_content
.data(),
316 non_sdch_content
.size());
317 filter
->FlushStreamBuffer(non_sdch_content
.size());
319 // Try to read output.
320 int output_bytes_or_buffer_size
= sizeof(output_buffer
);
321 Filter::FilterStatus status
= filter
->ReadData(output_buffer
,
322 &output_bytes_or_buffer_size
);
324 EXPECT_EQ(0, output_bytes_or_buffer_size
);
325 EXPECT_EQ(Filter::FILTER_ERROR
, status
);
328 TEST_F(SdchFilterTest
, ErrorOnBadReturnCodeWithHtml
) {
329 std::vector
<Filter::FilterType
> filter_types
;
330 // Selective a tentative filter (which can fall back to pass through).
331 filter_types
.push_back(Filter::FILTER_TYPE_SDCH_POSSIBLE
);
332 char output_buffer
[20];
333 // Response code needs to be 200 to allow a pass through.
334 filter_context()->SetResponseCode(403);
335 // Meta refresh will only appear for html content
336 filter_context()->SetMimeType("text/html");
337 std::string
url_string("http://ignore.com");
338 filter_context()->SetURL(GURL(url_string
));
339 scoped_ptr
<Filter
> filter(Filter::Factory(filter_types
, *filter_context()));
341 // Supply enough data to force a pass-through mode, which means we have
342 // provided more than 9 characters that can't be a dictionary hash.
343 std::string
non_sdch_content("This is not SDCH");
345 char* input_buffer
= filter
->stream_buffer()->data();
346 int input_buffer_size
= filter
->stream_buffer_size();
348 EXPECT_LT(static_cast<int>(non_sdch_content
.size()),
350 memcpy(input_buffer
, non_sdch_content
.data(),
351 non_sdch_content
.size());
352 filter
->FlushStreamBuffer(non_sdch_content
.size());
354 // Try to read output.
355 int output_bytes_or_buffer_size
= sizeof(output_buffer
);
356 Filter::FilterStatus status
= filter
->ReadData(output_buffer
,
357 &output_bytes_or_buffer_size
);
359 // We should have read a long and complicated meta-refresh request.
360 EXPECT_EQ(sizeof(output_buffer
),
361 static_cast<size_t>(output_bytes_or_buffer_size
));
362 // Check at least the prefix of the return.
363 EXPECT_EQ(0, strncmp(output_buffer
,
364 "<head><META HTTP-EQUIV=\"Refresh\" CONTENT=\"0\"></head>",
365 sizeof(output_buffer
)));
366 EXPECT_EQ(Filter::FILTER_OK
, status
);
369 TEST_F(SdchFilterTest
, BasicBadDictionary
) {
370 std::vector
<Filter::FilterType
> filter_types
;
371 filter_types
.push_back(Filter::FILTER_TYPE_SDCH
);
372 char output_buffer
[20];
373 std::string
url_string("http://ignore.com");
374 filter_context()->SetURL(GURL(url_string
));
375 scoped_ptr
<Filter
> filter(Filter::Factory(filter_types
, *filter_context()));
377 // Supply bogus data (which doesn't yet specify a full dictionary hash).
378 // Dictionary hash is 8 characters followed by a null.
379 std::string
dictionary_hash_prefix("123");
381 char* input_buffer
= filter
->stream_buffer()->data();
382 int input_buffer_size
= filter
->stream_buffer_size();
384 EXPECT_LT(static_cast<int>(dictionary_hash_prefix
.size()),
386 memcpy(input_buffer
, dictionary_hash_prefix
.data(),
387 dictionary_hash_prefix
.size());
388 filter
->FlushStreamBuffer(dictionary_hash_prefix
.size());
390 // With less than a dictionary specifier, try to read output.
391 int output_bytes_or_buffer_size
= sizeof(output_buffer
);
392 Filter::FilterStatus status
= filter
->ReadData(output_buffer
,
393 &output_bytes_or_buffer_size
);
395 EXPECT_EQ(0, output_bytes_or_buffer_size
);
396 EXPECT_EQ(Filter::FILTER_NEED_MORE_DATA
, status
);
398 // Provide enough data to complete *a* hash, but it is bogus, and not in our
399 // list of dictionaries, so the filter should error out immediately.
400 std::string
dictionary_hash_postfix("4abcd\0", 6);
402 CHECK_LT(dictionary_hash_postfix
.size(),
403 static_cast<size_t>(input_buffer_size
));
404 memcpy(input_buffer
, dictionary_hash_postfix
.data(),
405 dictionary_hash_postfix
.size());
406 filter
->FlushStreamBuffer(dictionary_hash_postfix
.size());
408 // With a non-existant dictionary specifier, try to read output.
409 output_bytes_or_buffer_size
= sizeof(output_buffer
);
410 status
= filter
->ReadData(output_buffer
, &output_bytes_or_buffer_size
);
412 EXPECT_EQ(0, output_bytes_or_buffer_size
);
413 EXPECT_EQ(Filter::FILTER_ERROR
, status
);
415 EXPECT_FALSE(sdch_manager_
->IsInSupportedDomain(GURL(url_string
)));
416 sdch_manager_
->ClearBlacklistings();
417 EXPECT_TRUE(sdch_manager_
->IsInSupportedDomain(GURL(url_string
)));
420 TEST_F(SdchFilterTest
, DictionaryAddOnce
) {
421 // Construct a valid SDCH dictionary from a VCDIFF dictionary.
422 const std::string kSampleDomain
= "sdchtest.com";
423 std::string
dictionary(NewSdchDictionary(kSampleDomain
));
425 std::string url_string
= "http://" + kSampleDomain
;
426 GURL
url(url_string
);
427 EXPECT_TRUE(AddSdchDictionary(dictionary
, url
));
429 // Check we can't add it twice.
430 EXPECT_FALSE(AddSdchDictionary(dictionary
, url
));
432 const std::string kSampleDomain2
= "sdchtest2.com";
434 // Don't test adding a second dictionary if our limits are tight.
435 if (SdchManager::kMaxDictionaryCount
> 1) {
436 // Construct a second SDCH dictionary from a VCDIFF dictionary.
437 std::string
dictionary2(NewSdchDictionary(kSampleDomain2
));
439 std::string url_string2
= "http://" + kSampleDomain2
;
440 GURL
url2(url_string2
);
441 EXPECT_TRUE(AddSdchDictionary(dictionary2
, url2
));
445 TEST_F(SdchFilterTest
, BasicDictionary
) {
446 // Construct a valid SDCH dictionary from a VCDIFF dictionary.
447 const std::string kSampleDomain
= "sdchtest.com";
448 std::string
dictionary(NewSdchDictionary(kSampleDomain
));
450 std::string url_string
= "http://" + kSampleDomain
;
452 GURL
url(url_string
);
453 EXPECT_TRUE(AddSdchDictionary(dictionary
, url
));
455 std::string
compressed(NewSdchCompressedData(dictionary
));
457 std::vector
<Filter::FilterType
> filter_types
;
458 filter_types
.push_back(Filter::FILTER_TYPE_SDCH
);
460 filter_context()->SetURL(url
);
462 scoped_ptr
<Filter
> filter(Filter::Factory(filter_types
, *filter_context()));
464 size_t feed_block_size
= 100;
465 size_t output_block_size
= 100;
467 EXPECT_TRUE(FilterTestData(compressed
, feed_block_size
, output_block_size
,
468 filter
.get(), &output
));
469 EXPECT_EQ(output
, expanded_
);
471 // Decode with really small buffers (size 1) to check for edge effects.
472 filter
.reset(Filter::Factory(filter_types
, *filter_context()));
475 output_block_size
= 1;
477 EXPECT_TRUE(FilterTestData(compressed
, feed_block_size
, output_block_size
,
478 filter
.get(), &output
));
479 EXPECT_EQ(output
, expanded_
);
482 TEST_F(SdchFilterTest
, NoDecodeHttps
) {
483 // Construct a valid SDCH dictionary from a VCDIFF dictionary.
484 const std::string kSampleDomain
= "sdchtest.com";
485 std::string
dictionary(NewSdchDictionary(kSampleDomain
));
487 std::string url_string
= "http://" + kSampleDomain
;
489 GURL
url(url_string
);
490 EXPECT_TRUE(AddSdchDictionary(dictionary
, url
));
492 std::string
compressed(NewSdchCompressedData(dictionary
));
494 std::vector
<Filter::FilterType
> filter_types
;
495 filter_types
.push_back(Filter::FILTER_TYPE_SDCH
);
497 filter_context()->SetURL(GURL("https://" + kSampleDomain
));
498 scoped_ptr
<Filter
> filter(Filter::Factory(filter_types
, *filter_context()));
500 const size_t feed_block_size(100);
501 const size_t output_block_size(100);
504 EXPECT_FALSE(FilterTestData(compressed
, feed_block_size
, output_block_size
,
505 filter
.get(), &output
));
508 // Current failsafe TODO/hack refuses to decode any content that doesn't use
509 // http as the scheme (see use of DICTIONARY_SELECTED_FOR_NON_HTTP).
510 // The following tests this blockage. Note that blacklisting results, so we
511 // we need separate tests for each of these.
512 TEST_F(SdchFilterTest
, NoDecodeFtp
) {
513 // Construct a valid SDCH dictionary from a VCDIFF dictionary.
514 const std::string kSampleDomain
= "sdchtest.com";
515 std::string
dictionary(NewSdchDictionary(kSampleDomain
));
517 std::string url_string
= "http://" + kSampleDomain
;
519 GURL
url(url_string
);
520 EXPECT_TRUE(AddSdchDictionary(dictionary
, url
));
522 std::string
compressed(NewSdchCompressedData(dictionary
));
524 std::vector
<Filter::FilterType
> filter_types
;
525 filter_types
.push_back(Filter::FILTER_TYPE_SDCH
);
527 filter_context()->SetURL(GURL("ftp://" + kSampleDomain
));
528 scoped_ptr
<Filter
> filter(Filter::Factory(filter_types
, *filter_context()));
530 const size_t feed_block_size(100);
531 const size_t output_block_size(100);
534 EXPECT_FALSE(FilterTestData(compressed
, feed_block_size
, output_block_size
,
535 filter
.get(), &output
));
538 TEST_F(SdchFilterTest
, NoDecodeFileColon
) {
539 // Construct a valid SDCH dictionary from a VCDIFF dictionary.
540 const std::string kSampleDomain
= "sdchtest.com";
541 std::string
dictionary(NewSdchDictionary(kSampleDomain
));
543 std::string url_string
= "http://" + kSampleDomain
;
545 GURL
url(url_string
);
546 EXPECT_TRUE(AddSdchDictionary(dictionary
, url
));
548 std::string
compressed(NewSdchCompressedData(dictionary
));
550 std::vector
<Filter::FilterType
> filter_types
;
551 filter_types
.push_back(Filter::FILTER_TYPE_SDCH
);
553 filter_context()->SetURL(GURL("file://" + kSampleDomain
));
554 scoped_ptr
<Filter
> filter(Filter::Factory(filter_types
, *filter_context()));
556 const size_t feed_block_size(100);
557 const size_t output_block_size(100);
560 EXPECT_FALSE(FilterTestData(compressed
, feed_block_size
, output_block_size
,
561 filter
.get(), &output
));
564 TEST_F(SdchFilterTest
, NoDecodeAboutColon
) {
565 // Construct a valid SDCH dictionary from a VCDIFF dictionary.
566 const std::string kSampleDomain
= "sdchtest.com";
567 std::string
dictionary(NewSdchDictionary(kSampleDomain
));
569 std::string url_string
= "http://" + kSampleDomain
;
571 GURL
url(url_string
);
572 EXPECT_TRUE(AddSdchDictionary(dictionary
, url
));
574 std::string
compressed(NewSdchCompressedData(dictionary
));
576 std::vector
<Filter::FilterType
> filter_types
;
577 filter_types
.push_back(Filter::FILTER_TYPE_SDCH
);
579 filter_context()->SetURL(GURL("about://" + kSampleDomain
));
580 scoped_ptr
<Filter
> filter(Filter::Factory(filter_types
, *filter_context()));
582 const size_t feed_block_size(100);
583 const size_t output_block_size(100);
586 EXPECT_FALSE(FilterTestData(compressed
, feed_block_size
, output_block_size
,
587 filter
.get(), &output
));
590 TEST_F(SdchFilterTest
, NoDecodeJavaScript
) {
591 // Construct a valid SDCH dictionary from a VCDIFF dictionary.
592 const std::string kSampleDomain
= "sdchtest.com";
593 std::string
dictionary(NewSdchDictionary(kSampleDomain
));
595 std::string url_string
= "http://" + kSampleDomain
;
597 GURL
url(url_string
);
598 EXPECT_TRUE(AddSdchDictionary(dictionary
, url
));
600 std::string
compressed(NewSdchCompressedData(dictionary
));
602 std::vector
<Filter::FilterType
> filter_types
;
603 filter_types
.push_back(Filter::FILTER_TYPE_SDCH
);
605 filter_context()->SetURL(GURL("javascript://" + kSampleDomain
));
606 scoped_ptr
<Filter
> filter(Filter::Factory(filter_types
, *filter_context()));
608 const size_t feed_block_size(100);
609 const size_t output_block_size(100);
612 EXPECT_FALSE(FilterTestData(compressed
, feed_block_size
, output_block_size
,
613 filter
.get(), &output
));
616 TEST_F(SdchFilterTest
, CanStillDecodeHttp
) {
617 // Construct a valid SDCH dictionary from a VCDIFF dictionary.
618 const std::string kSampleDomain
= "sdchtest.com";
619 std::string
dictionary(NewSdchDictionary(kSampleDomain
));
621 std::string url_string
= "http://" + kSampleDomain
;
623 GURL
url(url_string
);
624 EXPECT_TRUE(AddSdchDictionary(dictionary
, url
));
626 std::string
compressed(NewSdchCompressedData(dictionary
));
628 std::vector
<Filter::FilterType
> filter_types
;
629 filter_types
.push_back(Filter::FILTER_TYPE_SDCH
);
631 filter_context()->SetURL(GURL("http://" + kSampleDomain
));
632 scoped_ptr
<Filter
> filter(Filter::Factory(filter_types
, *filter_context()));
634 const size_t feed_block_size(100);
635 const size_t output_block_size(100);
638 EXPECT_TRUE(FilterTestData(compressed
, feed_block_size
, output_block_size
,
639 filter
.get(), &output
));
642 TEST_F(SdchFilterTest
, CrossDomainDictionaryUse
) {
643 // Construct a valid SDCH dictionary from a VCDIFF dictionary.
644 const std::string kSampleDomain
= "sdchtest.com";
645 std::string
dictionary(NewSdchDictionary(kSampleDomain
));
647 std::string url_string
= "http://" + kSampleDomain
;
649 GURL
url(url_string
);
650 EXPECT_TRUE(AddSdchDictionary(dictionary
, url
));
652 std::string
compressed(NewSdchCompressedData(dictionary
));
654 std::vector
<Filter::FilterType
> filter_types
;
655 filter_types
.push_back(Filter::FILTER_TYPE_SDCH
);
657 // Decode with content arriving from the "wrong" domain.
658 // This tests SdchManager::CanSet().
659 GURL
wrong_domain_url("http://www.wrongdomain.com");
660 filter_context()->SetURL(wrong_domain_url
);
661 scoped_ptr
<Filter
> filter(Filter::Factory(filter_types
, *filter_context()));
663 size_t feed_block_size
= 100;
664 size_t output_block_size
= 100;
666 EXPECT_FALSE(FilterTestData(compressed
, feed_block_size
, output_block_size
,
667 filter
.get(), &output
));
668 EXPECT_EQ(output
.size(), 0u); // No output written.
670 EXPECT_TRUE(sdch_manager_
->IsInSupportedDomain(GURL(url_string
)));
671 EXPECT_FALSE(sdch_manager_
->IsInSupportedDomain(wrong_domain_url
));
672 sdch_manager_
->ClearBlacklistings();
673 EXPECT_TRUE(sdch_manager_
->IsInSupportedDomain(wrong_domain_url
));
676 TEST_F(SdchFilterTest
, DictionaryPathValidation
) {
677 // Can't test path distinction between dictionaries if we aren't allowed
678 // more than one dictionary.
679 if (SdchManager::kMaxDictionaryCount
<= 1)
682 // Construct a valid SDCH dictionary from a VCDIFF dictionary.
683 const std::string kSampleDomain
= "sdchtest.com";
684 std::string
dictionary(NewSdchDictionary(kSampleDomain
));
686 std::string url_string
= "http://" + kSampleDomain
;
688 GURL
url(url_string
);
689 EXPECT_TRUE(AddSdchDictionary(dictionary
, url
));
691 // Create a dictionary with a path restriction, by prefixing dictionary.
692 const std::string
path("/special_path/bin");
693 std::string
dictionary_with_path("Path: " + path
+ "\n");
694 dictionary_with_path
.append(dictionary
);
695 GURL
url2(url_string
+ path
);
696 EXPECT_TRUE(AddSdchDictionary(dictionary_with_path
, url2
));
698 std::string
compressed_for_path(NewSdchCompressedData(dictionary_with_path
));
700 std::vector
<Filter::FilterType
> filter_types
;
701 filter_types
.push_back(Filter::FILTER_TYPE_SDCH
);
703 // Test decode the path data, arriving from a valid path.
704 filter_context()->SetURL(GURL(url_string
+ path
));
705 scoped_ptr
<Filter
> filter(Filter::Factory(filter_types
, *filter_context()));
707 size_t feed_block_size
= 100;
708 size_t output_block_size
= 100;
711 EXPECT_TRUE(FilterTestData(compressed_for_path
, feed_block_size
,
712 output_block_size
, filter
.get(), &output
));
713 EXPECT_EQ(output
, expanded_
);
715 // Test decode the path data, arriving from a invalid path.
716 filter_context()->SetURL(GURL(url_string
));
717 filter
.reset(Filter::Factory(filter_types
, *filter_context()));
719 feed_block_size
= 100;
720 output_block_size
= 100;
722 EXPECT_FALSE(FilterTestData(compressed_for_path
, feed_block_size
,
723 output_block_size
, filter
.get(), &output
));
724 EXPECT_EQ(output
.size(), 0u); // No output written.
726 EXPECT_FALSE(sdch_manager_
->IsInSupportedDomain(GURL(url_string
)));
727 sdch_manager_
->ClearBlacklistings();
728 EXPECT_TRUE(sdch_manager_
->IsInSupportedDomain(GURL(url_string
)));
731 TEST_F(SdchFilterTest
, DictionaryPortValidation
) {
732 // Can't test port distinction between dictionaries if we aren't allowed
733 // more than one dictionary.
734 if (SdchManager::kMaxDictionaryCount
<= 1)
737 // Construct a valid SDCH dictionary from a VCDIFF dictionary.
738 const std::string kSampleDomain
= "sdchtest.com";
739 std::string
dictionary(NewSdchDictionary(kSampleDomain
));
741 std::string url_string
= "http://" + kSampleDomain
;
743 GURL
url(url_string
);
744 EXPECT_TRUE(AddSdchDictionary(dictionary
, url
));
746 // Create a dictionary with a port restriction, by prefixing old dictionary.
747 const std::string
port("502");
748 std::string
dictionary_with_port("Port: " + port
+ "\n");
749 dictionary_with_port
.append("Port: 80\n"); // Add default port.
750 dictionary_with_port
.append(dictionary
);
751 EXPECT_TRUE(AddSdchDictionary(dictionary_with_port
,
752 GURL(url_string
+ ":" + port
)));
754 std::string
compressed_for_port(NewSdchCompressedData(dictionary_with_port
));
756 std::vector
<Filter::FilterType
> filter_types
;
757 filter_types
.push_back(Filter::FILTER_TYPE_SDCH
);
759 // Test decode the port data, arriving from a valid port.
760 filter_context()->SetURL(GURL(url_string
+ ":" + port
));
761 scoped_ptr
<Filter
> filter(Filter::Factory(filter_types
, *filter_context()));
763 size_t feed_block_size
= 100;
764 size_t output_block_size
= 100;
766 EXPECT_TRUE(FilterTestData(compressed_for_port
, feed_block_size
,
767 output_block_size
, filter
.get(), &output
));
768 EXPECT_EQ(output
, expanded_
);
770 // Test decode the port data, arriving from a valid (default) port.
771 filter_context()->SetURL(GURL(url_string
)); // Default port.
772 filter
.reset(Filter::Factory(filter_types
, *filter_context()));
774 feed_block_size
= 100;
775 output_block_size
= 100;
777 EXPECT_TRUE(FilterTestData(compressed_for_port
, feed_block_size
,
778 output_block_size
, filter
.get(), &output
));
779 EXPECT_EQ(output
, expanded_
);
781 // Test decode the port data, arriving from a invalid port.
782 filter_context()->SetURL(GURL(url_string
+ ":" + port
+ "1"));
783 filter
.reset(Filter::Factory(filter_types
, *filter_context()));
785 feed_block_size
= 100;
786 output_block_size
= 100;
788 EXPECT_FALSE(FilterTestData(compressed_for_port
, feed_block_size
,
789 output_block_size
, filter
.get(), &output
));
790 EXPECT_EQ(output
.size(), 0u); // No output written.
792 EXPECT_FALSE(sdch_manager_
->IsInSupportedDomain(GURL(url_string
)));
793 sdch_manager_
->ClearBlacklistings();
794 EXPECT_TRUE(sdch_manager_
->IsInSupportedDomain(GURL(url_string
)));
797 //------------------------------------------------------------------------------
798 // Helper function to perform gzip compression of data.
800 static std::string
gzip_compress(const std::string
&input
) {
801 z_stream zlib_stream
;
802 memset(&zlib_stream
, 0, sizeof(zlib_stream
));
806 code
= deflateInit2(&zlib_stream
, Z_DEFAULT_COMPRESSION
, Z_DEFLATED
,
811 CHECK_EQ(Z_OK
, code
);
813 // Fill in zlib control block
814 zlib_stream
.next_in
= bit_cast
<Bytef
*>(input
.data());
815 zlib_stream
.avail_in
= input
.size();
817 // Assume we can compress into similar buffer (add 100 bytes to be sure).
818 size_t gzip_compressed_length
= zlib_stream
.avail_in
+ 100;
819 scoped_ptr
<char[]> gzip_compressed(new char[gzip_compressed_length
]);
820 zlib_stream
.next_out
= bit_cast
<Bytef
*>(gzip_compressed
.get());
821 zlib_stream
.avail_out
= gzip_compressed_length
;
823 // The GZIP header (see RFC 1952):
824 // +---+---+---+---+---+---+---+---+---+---+
825 // |ID1|ID2|CM |FLG| MTIME |XFL|OS |
826 // +---+---+---+---+---+---+---+---+---+---+
829 // CM \010 (compression method == DEFLATE)
830 // FLG \000 (special flags that we do not support)
831 // MTIME Unix format modification time (0 means not available)
832 // XFL 2-4? DEFLATE flags
833 // OS ???? Operating system indicator (255 means unknown)
835 // Header value we generate:
836 const char kGZipHeader
[] = { '\037', '\213', '\010', '\000', '\000',
837 '\000', '\000', '\000', '\002', '\377' };
838 CHECK_GT(zlib_stream
.avail_out
, sizeof(kGZipHeader
));
839 memcpy(zlib_stream
.next_out
, kGZipHeader
, sizeof(kGZipHeader
));
840 zlib_stream
.next_out
+= sizeof(kGZipHeader
);
841 zlib_stream
.avail_out
-= sizeof(kGZipHeader
);
844 code
= deflate(&zlib_stream
, Z_FINISH
);
845 gzip_compressed_length
-= zlib_stream
.avail_out
;
846 std::string
compressed(gzip_compressed
.get(), gzip_compressed_length
);
847 deflateEnd(&zlib_stream
);
851 //------------------------------------------------------------------------------
853 class SdchFilterChainingTest
{
855 static Filter
* Factory(const std::vector
<Filter::FilterType
>& types
,
856 const FilterContext
& context
, int size
) {
857 return Filter::FactoryForTests(types
, context
, size
);
861 // Test that filters can be cascaded (chained) so that the output of one filter
862 // is processed by the next one. This is most critical for SDCH, which is
863 // routinely followed by gzip (during encoding). The filter we'll test for will
864 // do the gzip decoding first, and then decode the SDCH content.
865 TEST_F(SdchFilterTest
, FilterChaining
) {
866 // Construct a valid SDCH dictionary from a VCDIFF dictionary.
867 const std::string kSampleDomain
= "sdchtest.com";
868 std::string
dictionary(NewSdchDictionary(kSampleDomain
));
870 std::string url_string
= "http://" + kSampleDomain
;
872 GURL
url(url_string
);
873 EXPECT_TRUE(AddSdchDictionary(dictionary
, url
));
875 std::string
sdch_compressed(NewSdchCompressedData(dictionary
));
877 // Use Gzip to compress the sdch sdch_compressed data.
878 std::string gzip_compressed_sdch
= gzip_compress(sdch_compressed
);
880 // Construct a chained filter.
881 std::vector
<Filter::FilterType
> filter_types
;
882 filter_types
.push_back(Filter::FILTER_TYPE_SDCH
);
883 filter_types
.push_back(Filter::FILTER_TYPE_GZIP
);
885 // First try with a large buffer (larger than test input, or compressed data).
886 const size_t kLargeInputBufferSize(1000); // Used internally in filters.
887 CHECK_GT(kLargeInputBufferSize
, gzip_compressed_sdch
.size());
888 CHECK_GT(kLargeInputBufferSize
, sdch_compressed
.size());
889 CHECK_GT(kLargeInputBufferSize
, expanded_
.size());
890 filter_context()->SetURL(url
);
891 scoped_ptr
<Filter
> filter(
892 SdchFilterChainingTest::Factory(filter_types
, *filter_context(),
893 kLargeInputBufferSize
));
894 EXPECT_EQ(static_cast<int>(kLargeInputBufferSize
),
895 filter
->stream_buffer_size());
897 // Verify that chained filter is waiting for data.
898 char tiny_output_buffer
[10];
899 int tiny_output_size
= sizeof(tiny_output_buffer
);
900 EXPECT_EQ(Filter::FILTER_NEED_MORE_DATA
,
901 filter
->ReadData(tiny_output_buffer
, &tiny_output_size
));
903 // Make chain process all data.
904 size_t feed_block_size
= kLargeInputBufferSize
;
905 size_t output_block_size
= kLargeInputBufferSize
;
907 EXPECT_TRUE(FilterTestData(gzip_compressed_sdch
, feed_block_size
,
908 output_block_size
, filter
.get(), &output
));
909 EXPECT_EQ(output
, expanded_
);
911 // Next try with a mid-sized internal buffer size.
912 const size_t kMidSizedInputBufferSize(100);
913 // Buffer should be big enough to swallow whole gzip content.
914 CHECK_GT(kMidSizedInputBufferSize
, gzip_compressed_sdch
.size());
915 // Buffer should be small enough that entire SDCH content can't fit.
916 // We'll go even further, and force the chain to flush the buffer between the
917 // two filters more than once (that is why we multiply by 2).
918 CHECK_LT(kMidSizedInputBufferSize
* 2, sdch_compressed
.size());
919 filter_context()->SetURL(url
);
921 SdchFilterChainingTest::Factory(filter_types
, *filter_context(),
922 kMidSizedInputBufferSize
));
923 EXPECT_EQ(static_cast<int>(kMidSizedInputBufferSize
),
924 filter
->stream_buffer_size());
926 feed_block_size
= kMidSizedInputBufferSize
;
927 output_block_size
= kMidSizedInputBufferSize
;
929 EXPECT_TRUE(FilterTestData(gzip_compressed_sdch
, feed_block_size
,
930 output_block_size
, filter
.get(), &output
));
931 EXPECT_EQ(output
, expanded_
);
933 // Next try with a tiny input and output buffer to cover edge effects.
934 filter
.reset(SdchFilterChainingTest::Factory(filter_types
, *filter_context(),
935 kLargeInputBufferSize
));
936 EXPECT_EQ(static_cast<int>(kLargeInputBufferSize
),
937 filter
->stream_buffer_size());
940 output_block_size
= 1;
942 EXPECT_TRUE(FilterTestData(gzip_compressed_sdch
, feed_block_size
,
943 output_block_size
, filter
.get(), &output
));
944 EXPECT_EQ(output
, expanded_
);
947 TEST_F(SdchFilterTest
, DefaultGzipIfSdch
) {
948 // Construct a valid SDCH dictionary from a VCDIFF dictionary.
949 const std::string kSampleDomain
= "sdchtest.com";
950 std::string
dictionary(NewSdchDictionary(kSampleDomain
));
952 std::string url_string
= "http://" + kSampleDomain
;
954 GURL
url(url_string
);
955 EXPECT_TRUE(AddSdchDictionary(dictionary
, url
));
957 std::string
sdch_compressed(NewSdchCompressedData(dictionary
));
959 // Use Gzip to compress the sdch sdch_compressed data.
960 std::string gzip_compressed_sdch
= gzip_compress(sdch_compressed
);
962 // Only claim to have sdch content, but really use the gzipped sdch content.
963 // System should automatically add the missing (optional) gzip.
964 std::vector
<Filter::FilterType
> filter_types
;
965 filter_types
.push_back(Filter::FILTER_TYPE_SDCH
);
967 filter_context()->SetMimeType("anything/mime");
968 filter_context()->SetSdchResponse(true);
969 Filter::FixupEncodingTypes(*filter_context(), &filter_types
);
970 ASSERT_EQ(filter_types
.size(), 2u);
971 EXPECT_EQ(filter_types
[0], Filter::FILTER_TYPE_SDCH
);
972 EXPECT_EQ(filter_types
[1], Filter::FILTER_TYPE_GZIP_HELPING_SDCH
);
974 // First try with a large buffer (larger than test input, or compressed data).
975 filter_context()->SetURL(url
);
976 scoped_ptr
<Filter
> filter(Filter::Factory(filter_types
, *filter_context()));
979 // Verify that chained filter is waiting for data.
980 char tiny_output_buffer
[10];
981 int tiny_output_size
= sizeof(tiny_output_buffer
);
982 EXPECT_EQ(Filter::FILTER_NEED_MORE_DATA
,
983 filter
->ReadData(tiny_output_buffer
, &tiny_output_size
));
985 size_t feed_block_size
= 100;
986 size_t output_block_size
= 100;
988 EXPECT_TRUE(FilterTestData(gzip_compressed_sdch
, feed_block_size
,
989 output_block_size
, filter
.get(), &output
));
990 EXPECT_EQ(output
, expanded_
);
992 // Next try with a tiny buffer to cover edge effects.
993 filter
.reset(Filter::Factory(filter_types
, *filter_context()));
996 output_block_size
= 1;
998 EXPECT_TRUE(FilterTestData(gzip_compressed_sdch
, feed_block_size
,
999 output_block_size
, filter
.get(), &output
));
1000 EXPECT_EQ(output
, expanded_
);
1003 TEST_F(SdchFilterTest
, AcceptGzipSdchIfGzip
) {
1004 // Construct a valid SDCH dictionary from a VCDIFF dictionary.
1005 const std::string kSampleDomain
= "sdchtest.com";
1006 std::string
dictionary(NewSdchDictionary(kSampleDomain
));
1008 std::string url_string
= "http://" + kSampleDomain
;
1010 GURL
url(url_string
);
1011 EXPECT_TRUE(AddSdchDictionary(dictionary
, url
));
1013 std::string
sdch_compressed(NewSdchCompressedData(dictionary
));
1015 // Use Gzip to compress the sdch sdch_compressed data.
1016 std::string gzip_compressed_sdch
= gzip_compress(sdch_compressed
);
1018 // Some proxies strip the content encoding statement down to a mere gzip, but
1019 // pass through the original content (with full sdch,gzip encoding).
1020 // Only claim to have gzip content, but really use the gzipped sdch content.
1021 // System should automatically add the missing (optional) sdch.
1022 std::vector
<Filter::FilterType
> filter_types
;
1023 filter_types
.push_back(Filter::FILTER_TYPE_GZIP
);
1025 filter_context()->SetMimeType("anything/mime");
1026 filter_context()->SetSdchResponse(true);
1027 Filter::FixupEncodingTypes(*filter_context(), &filter_types
);
1028 ASSERT_EQ(filter_types
.size(), 3u);
1029 EXPECT_EQ(filter_types
[0], Filter::FILTER_TYPE_SDCH_POSSIBLE
);
1030 EXPECT_EQ(filter_types
[1], Filter::FILTER_TYPE_GZIP_HELPING_SDCH
);
1031 EXPECT_EQ(filter_types
[2], Filter::FILTER_TYPE_GZIP
);
1033 // First try with a large buffer (larger than test input, or compressed data).
1034 filter_context()->SetURL(url
);
1035 scoped_ptr
<Filter
> filter(Filter::Factory(filter_types
, *filter_context()));
1038 // Verify that chained filter is waiting for data.
1039 char tiny_output_buffer
[10];
1040 int tiny_output_size
= sizeof(tiny_output_buffer
);
1041 EXPECT_EQ(Filter::FILTER_NEED_MORE_DATA
,
1042 filter
->ReadData(tiny_output_buffer
, &tiny_output_size
));
1044 size_t feed_block_size
= 100;
1045 size_t output_block_size
= 100;
1047 EXPECT_TRUE(FilterTestData(gzip_compressed_sdch
, feed_block_size
,
1048 output_block_size
, filter
.get(), &output
));
1049 EXPECT_EQ(output
, expanded_
);
1051 // Next try with a tiny buffer to cover edge effects.
1052 filter
.reset(Filter::Factory(filter_types
, *filter_context()));
1054 feed_block_size
= 1;
1055 output_block_size
= 1;
1057 EXPECT_TRUE(FilterTestData(gzip_compressed_sdch
, feed_block_size
,
1058 output_block_size
, filter
.get(), &output
));
1059 EXPECT_EQ(output
, expanded_
);
1062 TEST_F(SdchFilterTest
, DefaultSdchGzipIfEmpty
) {
1063 // Construct a valid SDCH dictionary from a VCDIFF dictionary.
1064 const std::string kSampleDomain
= "sdchtest.com";
1065 std::string
dictionary(NewSdchDictionary(kSampleDomain
));
1067 std::string url_string
= "http://" + kSampleDomain
;
1069 GURL
url(url_string
);
1070 EXPECT_TRUE(AddSdchDictionary(dictionary
, url
));
1072 std::string
sdch_compressed(NewSdchCompressedData(dictionary
));
1074 // Use Gzip to compress the sdch sdch_compressed data.
1075 std::string gzip_compressed_sdch
= gzip_compress(sdch_compressed
);
1077 // Only claim to have non-encoded content, but really use the gzipped sdch
1079 // System should automatically add the missing (optional) sdch,gzip.
1080 std::vector
<Filter::FilterType
> filter_types
;
1082 filter_context()->SetMimeType("anything/mime");
1083 filter_context()->SetSdchResponse(true);
1084 Filter::FixupEncodingTypes(*filter_context(), &filter_types
);
1085 ASSERT_EQ(filter_types
.size(), 2u);
1086 EXPECT_EQ(filter_types
[0], Filter::FILTER_TYPE_SDCH_POSSIBLE
);
1087 EXPECT_EQ(filter_types
[1], Filter::FILTER_TYPE_GZIP_HELPING_SDCH
);
1089 // First try with a large buffer (larger than test input, or compressed data).
1090 filter_context()->SetURL(url
);
1091 scoped_ptr
<Filter
> filter(Filter::Factory(filter_types
, *filter_context()));
1094 // Verify that chained filter is waiting for data.
1095 char tiny_output_buffer
[10];
1096 int tiny_output_size
= sizeof(tiny_output_buffer
);
1097 EXPECT_EQ(Filter::FILTER_NEED_MORE_DATA
,
1098 filter
->ReadData(tiny_output_buffer
, &tiny_output_size
));
1100 size_t feed_block_size
= 100;
1101 size_t output_block_size
= 100;
1103 EXPECT_TRUE(FilterTestData(gzip_compressed_sdch
, feed_block_size
,
1104 output_block_size
, filter
.get(), &output
));
1105 EXPECT_EQ(output
, expanded_
);
1107 // Next try with a tiny buffer to cover edge effects.
1108 filter
.reset(Filter::Factory(filter_types
, *filter_context()));
1110 feed_block_size
= 1;
1111 output_block_size
= 1;
1113 EXPECT_TRUE(FilterTestData(gzip_compressed_sdch
, feed_block_size
,
1114 output_block_size
, filter
.get(), &output
));
1115 EXPECT_EQ(output
, expanded_
);
1118 TEST_F(SdchFilterTest
, AcceptGzipGzipSdchIfGzip
) {
1119 // Construct a valid SDCH dictionary from a VCDIFF dictionary.
1120 const std::string kSampleDomain
= "sdchtest.com";
1121 std::string
dictionary(NewSdchDictionary(kSampleDomain
));
1123 std::string url_string
= "http://" + kSampleDomain
;
1125 GURL
url(url_string
);
1126 EXPECT_TRUE(AddSdchDictionary(dictionary
, url
));
1128 std::string
sdch_compressed(NewSdchCompressedData(dictionary
));
1130 // Vodaphone (UK) Mobile Broadband provides double gzipped sdch with a content
1131 // encoding of merely gzip (apparently, only listing the extra level of
1132 // wrapper compression they added, but discarding the actual content encoding.
1133 // Use Gzip to double compress the sdch sdch_compressed data.
1134 std::string double_gzip_compressed_sdch
= gzip_compress(gzip_compress(
1137 // Only claim to have gzip content, but really use the double gzipped sdch
1139 // System should automatically add the missing (optional) sdch, gzip decoders.
1140 std::vector
<Filter::FilterType
> filter_types
;
1141 filter_types
.push_back(Filter::FILTER_TYPE_GZIP
);
1143 filter_context()->SetMimeType("anything/mime");
1144 filter_context()->SetSdchResponse(true);
1145 Filter::FixupEncodingTypes(*filter_context(), &filter_types
);
1146 ASSERT_EQ(filter_types
.size(), 3u);
1147 EXPECT_EQ(filter_types
[0], Filter::FILTER_TYPE_SDCH_POSSIBLE
);
1148 EXPECT_EQ(filter_types
[1], Filter::FILTER_TYPE_GZIP_HELPING_SDCH
);
1149 EXPECT_EQ(filter_types
[2], Filter::FILTER_TYPE_GZIP
);
1151 // First try with a large buffer (larger than test input, or compressed data).
1152 filter_context()->SetURL(url
);
1153 scoped_ptr
<Filter
> filter(Filter::Factory(filter_types
, *filter_context()));
1155 // Verify that chained filter is waiting for data.
1156 char tiny_output_buffer
[10];
1157 int tiny_output_size
= sizeof(tiny_output_buffer
);
1158 EXPECT_EQ(Filter::FILTER_NEED_MORE_DATA
,
1159 filter
->ReadData(tiny_output_buffer
, &tiny_output_size
));
1161 size_t feed_block_size
= 100;
1162 size_t output_block_size
= 100;
1164 EXPECT_TRUE(FilterTestData(double_gzip_compressed_sdch
, feed_block_size
,
1165 output_block_size
, filter
.get(), &output
));
1166 EXPECT_EQ(output
, expanded_
);
1168 // Next try with a tiny buffer to cover edge effects.
1169 filter
.reset(Filter::Factory(filter_types
, *filter_context()));
1171 feed_block_size
= 1;
1172 output_block_size
= 1;
1174 EXPECT_TRUE(FilterTestData(double_gzip_compressed_sdch
, feed_block_size
,
1175 output_block_size
, filter
.get(), &output
));
1176 EXPECT_EQ(output
, expanded_
);