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 "base/test/simple_test_clock.h"
14 #include "net/base/io_buffer.h"
15 #include "net/filter/mock_filter_context.h"
16 #include "net/filter/sdch_filter.h"
17 #include "net/url_request/url_request_context.h"
18 #include "net/url_request/url_request_http_job.h"
19 #include "testing/gtest/include/gtest/gtest.h"
20 #include "third_party/zlib/zlib.h"
24 //------------------------------------------------------------------------------
25 // Provide sample data and compression results with a sample VCDIFF dictionary.
26 // Note an SDCH dictionary has extra meta-data before the VCDIFF dictionary.
27 static const char kTestVcdiffDictionary
[] = "DictionaryFor"
28 "SdchCompression1SdchCompression2SdchCompression3SdchCompression\n";
29 // Pre-compression test data. Note that we pad with a lot of highly gzip
30 // compressible content to help to exercise the chaining pipeline. That is why
31 // there are a PILE of zeros at the start and end.
32 // This will ensure that gzip compressed data can be fed to the chain in one
33 // gulp, but (with careful selection of intermediate buffers) that it takes
34 // several sdch buffers worth of data to satisfy the sdch filter. See detailed
35 // CHECK() calls in FilterChaining test for specifics.
36 static const char kTestData
[] = "0000000000000000000000000000000000000000000000"
37 "0000000000000000000000000000TestData "
38 "SdchCompression1SdchCompression2SdchCompression3SdchCompression"
39 "00000000000000000000000000000000000000000000000000000000000000000000000000"
40 "000000000000000000000000000000000000000\n";
42 // Note SDCH compressed data will include a reference to the SDCH dictionary.
43 static const char kSdchCompressedTestData
[] =
44 "\326\303\304\0\0\001M\0\201S\202\004\0\201E\006\001"
45 "00000000000000000000000000000000000000000000000000000000000000000000000000"
46 "TestData 00000000000000000000000000000000000000000000000000000000000000000"
47 "000000000000000000000000000000000000000000000000\n\001S\023\077\001r\r";
49 //------------------------------------------------------------------------------
51 class SdchFilterTest
: public testing::Test
{
54 : test_vcdiff_dictionary_(kTestVcdiffDictionary
,
55 sizeof(kTestVcdiffDictionary
) - 1),
56 vcdiff_compressed_data_(kSdchCompressedTestData
,
57 sizeof(kSdchCompressedTestData
) - 1),
58 expanded_(kTestData
, sizeof(kTestData
) - 1),
59 sdch_manager_(new SdchManager
),
60 filter_context_(new MockFilterContext
) {
61 URLRequestContext
* url_request_context
=
62 filter_context_
->GetModifiableURLRequestContext();
64 url_request_context
->set_sdch_manager(sdch_manager_
.get());
67 // Attempt to add a dictionary to the manager and probe for success or
69 bool AddSdchDictionary(const std::string
& dictionary_text
,
71 return sdch_manager_
->AddSdchDictionary(dictionary_text
, gurl
) == SDCH_OK
;
74 MockFilterContext
* filter_context() { return filter_context_
.get(); }
76 // Sets both the GURL and the SDCH response for a filter context.
77 void SetupFilterContextWithGURL(GURL url
) {
78 filter_context_
->SetURL(url
);
79 filter_context_
->SetSdchResponse(
80 sdch_manager_
->GetDictionarySet(url
).Pass());
83 std::string
NewSdchCompressedData(const std::string dictionary
) {
84 std::string client_hash
;
85 std::string server_hash
;
86 SdchManager::GenerateHash(dictionary
, &client_hash
, &server_hash
);
88 // Build compressed data that refers to our dictionary.
89 std::string
compressed(server_hash
);
90 compressed
.append("\0", 1);
91 compressed
.append(vcdiff_compressed_data_
);
95 const std::string test_vcdiff_dictionary_
;
96 const std::string vcdiff_compressed_data_
;
97 const std::string expanded_
; // Desired final, decompressed data.
99 scoped_ptr
<SdchManager
> sdch_manager_
;
100 scoped_ptr
<MockFilterContext
> filter_context_
;
103 //------------------------------------------------------------------------------
106 TEST_F(SdchFilterTest
, Hashing
) {
107 std::string client_hash
, server_hash
;
108 std::string
dictionary("test contents");
109 SdchManager::GenerateHash(dictionary
, &client_hash
, &server_hash
);
111 EXPECT_EQ(client_hash
, "lMQBjS3P");
112 EXPECT_EQ(server_hash
, "MyciMVll");
116 //------------------------------------------------------------------------------
117 // Provide a generic helper function for trying to filter data.
118 // This function repeatedly calls the filter to process data, until the entire
119 // source is consumed. The return value from the filter is appended to output.
120 // This allows us to vary input and output block sizes in order to test for edge
121 // effects (boundary effects?) during the filtering process.
122 // This function provides data to the filter in blocks of no-more-than the
123 // specified input_block_length. It allows the filter to fill no more than
124 // output_buffer_length in any one call to proccess (a.k.a., Read) data, and
125 // concatenates all these little output blocks into the singular output string.
126 static bool FilterTestData(const std::string
& source
,
127 size_t input_block_length
,
128 const size_t output_buffer_length
,
129 Filter
* filter
, std::string
* output
) {
130 CHECK_GT(input_block_length
, 0u);
131 Filter::FilterStatus
status(Filter::FILTER_NEED_MORE_DATA
);
132 size_t source_index
= 0;
133 scoped_ptr
<char[]> output_buffer(new char[output_buffer_length
]);
134 size_t input_amount
= std::min(input_block_length
,
135 static_cast<size_t>(filter
->stream_buffer_size()));
138 int copy_amount
= std::min(input_amount
, source
.size() - source_index
);
139 if (copy_amount
> 0 && status
== Filter::FILTER_NEED_MORE_DATA
) {
140 memcpy(filter
->stream_buffer()->data(), source
.data() + source_index
,
142 filter
->FlushStreamBuffer(copy_amount
);
143 source_index
+= copy_amount
;
145 int buffer_length
= output_buffer_length
;
146 status
= filter
->ReadData(output_buffer
.get(), &buffer_length
);
147 output
->append(output_buffer
.get(), buffer_length
);
148 if (status
== Filter::FILTER_ERROR
)
150 // Callers assume that FILTER_OK with no output buffer means FILTER_DONE.
151 if (Filter::FILTER_OK
== status
&& 0 == buffer_length
)
153 if (copy_amount
== 0 && buffer_length
== 0)
157 //------------------------------------------------------------------------------
158 static std::string
NewSdchDictionary(const std::string
& domain
) {
159 std::string dictionary
;
160 if (!domain
.empty()) {
161 dictionary
.append("Domain: ");
162 dictionary
.append(domain
);
163 dictionary
.append("\n");
165 dictionary
.append("\n");
166 dictionary
.append(kTestVcdiffDictionary
, sizeof(kTestVcdiffDictionary
) - 1);
170 static std::string
NewSdchExpiredDictionary(const std::string
& domain
) {
171 std::string dictionary
;
172 if (!domain
.empty()) {
173 dictionary
.append("Domain: ");
174 dictionary
.append(domain
);
175 dictionary
.append("\n");
177 dictionary
.append("Max-Age: 0\n");
178 dictionary
.append("\n");
179 dictionary
.append(kTestVcdiffDictionary
, sizeof(kTestVcdiffDictionary
) - 1);
183 //------------------------------------------------------------------------------
185 TEST_F(SdchFilterTest
, EmptyInputOk
) {
186 std::vector
<Filter::FilterType
> filter_types
;
187 filter_types
.push_back(Filter::FILTER_TYPE_SDCH
);
188 char output_buffer
[20];
189 std::string
url_string("http://ignore.com");
190 filter_context()->SetURL(GURL(url_string
));
191 scoped_ptr
<Filter
> filter(Filter::Factory(filter_types
, *filter_context()));
193 // With no input data, try to read output.
194 int output_bytes_or_buffer_size
= sizeof(output_buffer
);
195 Filter::FilterStatus status
= filter
->ReadData(output_buffer
,
196 &output_bytes_or_buffer_size
);
198 EXPECT_EQ(0, output_bytes_or_buffer_size
);
199 EXPECT_EQ(Filter::FILTER_NEED_MORE_DATA
, status
);
202 // Make sure that the filter context has everything that might be
203 // nuked from it during URLRequest teardown before the SdchFilter
205 TEST_F(SdchFilterTest
, SparseContextOk
) {
206 std::vector
<Filter::FilterType
> filter_types
;
207 filter_types
.push_back(Filter::FILTER_TYPE_SDCH
);
208 char output_buffer
[20];
209 std::string
url_string("http://ignore.com");
210 filter_context()->SetURL(GURL(url_string
));
211 scoped_ptr
<Filter
> filter(Filter::Factory(filter_types
, *filter_context()));
213 // With no input data, try to read output.
214 int output_bytes_or_buffer_size
= sizeof(output_buffer
);
215 Filter::FilterStatus status
= filter
->ReadData(output_buffer
,
216 &output_bytes_or_buffer_size
);
218 EXPECT_EQ(0, output_bytes_or_buffer_size
);
219 EXPECT_EQ(Filter::FILTER_NEED_MORE_DATA
, status
);
221 // Partially tear down context. Anything that goes through request()
222 // without checking it for null in the URLRequestJob::HttpFilterContext
223 // implementation is suspect. Everything that does check it for null should
224 // return null. This is to test for incorrectly relying on filter_context()
225 // from the SdchFilter destructor.
226 filter_context()->NukeUnstableInterfaces();
229 TEST_F(SdchFilterTest
, PassThroughWhenTentative
) {
230 std::vector
<Filter::FilterType
> filter_types
;
231 // Selective a tentative filter (which can fall back to pass through).
232 filter_types
.push_back(Filter::FILTER_TYPE_GZIP_HELPING_SDCH
);
233 char output_buffer
[20];
234 // Response code needs to be 200 to allow a pass through.
235 filter_context()->SetResponseCode(200);
236 std::string
url_string("http://ignore.com");
237 filter_context()->SetURL(GURL(url_string
));
238 scoped_ptr
<Filter
> filter(Filter::Factory(filter_types
, *filter_context()));
240 // Supply enough data to force a pass-through mode..
241 std::string
non_gzip_content("not GZIPed data");
243 char* input_buffer
= filter
->stream_buffer()->data();
244 int input_buffer_size
= filter
->stream_buffer_size();
246 EXPECT_LT(static_cast<int>(non_gzip_content
.size()),
248 memcpy(input_buffer
, non_gzip_content
.data(),
249 non_gzip_content
.size());
250 filter
->FlushStreamBuffer(non_gzip_content
.size());
252 // Try to read output.
253 int output_bytes_or_buffer_size
= sizeof(output_buffer
);
254 Filter::FilterStatus status
= filter
->ReadData(output_buffer
,
255 &output_bytes_or_buffer_size
);
257 EXPECT_EQ(non_gzip_content
.size(),
258 static_cast<size_t>(output_bytes_or_buffer_size
));
259 ASSERT_GT(sizeof(output_buffer
),
260 static_cast<size_t>(output_bytes_or_buffer_size
));
261 output_buffer
[output_bytes_or_buffer_size
] = '\0';
262 EXPECT_TRUE(non_gzip_content
== output_buffer
);
263 EXPECT_EQ(Filter::FILTER_NEED_MORE_DATA
, status
);
266 TEST_F(SdchFilterTest
, RefreshBadReturnCode
) {
267 std::vector
<Filter::FilterType
> filter_types
;
268 // Selective a tentative filter (which can fall back to pass through).
269 filter_types
.push_back(Filter::FILTER_TYPE_SDCH_POSSIBLE
);
270 char output_buffer
[20];
271 // Response code needs to be 200 to allow a pass through.
272 filter_context()->SetResponseCode(403);
273 // Meta refresh will only appear for html content
274 filter_context()->SetMimeType("text/html");
275 std::string
url_string("http://ignore.com");
276 filter_context()->SetURL(GURL(url_string
));
277 scoped_ptr
<Filter
> filter(Filter::Factory(filter_types
, *filter_context()));
279 // Supply enough data to force a pass-through mode, which means we have
280 // provided more than 9 characters that can't be a dictionary hash.
281 std::string
non_sdch_content("This is not SDCH");
283 char* input_buffer
= filter
->stream_buffer()->data();
284 int input_buffer_size
= filter
->stream_buffer_size();
286 EXPECT_LT(static_cast<int>(non_sdch_content
.size()),
288 memcpy(input_buffer
, non_sdch_content
.data(),
289 non_sdch_content
.size());
290 filter
->FlushStreamBuffer(non_sdch_content
.size());
292 // Try to read output.
293 int output_bytes_or_buffer_size
= sizeof(output_buffer
);
294 Filter::FilterStatus status
= filter
->ReadData(output_buffer
,
295 &output_bytes_or_buffer_size
);
297 // We should have read a long and complicated meta-refresh request.
298 EXPECT_TRUE(sizeof(output_buffer
) == output_bytes_or_buffer_size
);
299 // Check at least the prefix of the return.
300 EXPECT_EQ(0, strncmp(output_buffer
,
301 "<head><META HTTP-EQUIV=\"Refresh\" CONTENT=\"0\"></head>",
302 sizeof(output_buffer
)));
303 EXPECT_EQ(Filter::FILTER_OK
, status
);
306 TEST_F(SdchFilterTest
, ErrorOnBadReturnCode
) {
307 std::vector
<Filter::FilterType
> filter_types
;
308 // Selective a tentative filter (which can fall back to pass through).
309 filter_types
.push_back(Filter::FILTER_TYPE_SDCH_POSSIBLE
);
310 char output_buffer
[20];
311 // Response code needs to be 200 to allow a pass through.
312 filter_context()->SetResponseCode(403);
313 // Meta refresh will only appear for html content, so set to something else
314 // to induce an error (we can't meta refresh).
315 filter_context()->SetMimeType("anything");
316 std::string
url_string("http://ignore.com");
317 filter_context()->SetURL(GURL(url_string
));
318 scoped_ptr
<Filter
> filter(Filter::Factory(filter_types
, *filter_context()));
320 // Supply enough data to force a pass-through mode, which means we have
321 // provided more than 9 characters that can't be a dictionary hash.
322 std::string
non_sdch_content("This is not SDCH");
324 char* input_buffer
= filter
->stream_buffer()->data();
325 int input_buffer_size
= filter
->stream_buffer_size();
327 EXPECT_LT(static_cast<int>(non_sdch_content
.size()),
329 memcpy(input_buffer
, non_sdch_content
.data(),
330 non_sdch_content
.size());
331 filter
->FlushStreamBuffer(non_sdch_content
.size());
333 // Try to read output.
334 int output_bytes_or_buffer_size
= sizeof(output_buffer
);
335 Filter::FilterStatus status
= filter
->ReadData(output_buffer
,
336 &output_bytes_or_buffer_size
);
338 EXPECT_EQ(0, output_bytes_or_buffer_size
);
339 EXPECT_EQ(Filter::FILTER_ERROR
, status
);
342 TEST_F(SdchFilterTest
, ErrorOnBadReturnCodeWithHtml
) {
343 std::vector
<Filter::FilterType
> filter_types
;
344 // Selective a tentative filter (which can fall back to pass through).
345 filter_types
.push_back(Filter::FILTER_TYPE_SDCH_POSSIBLE
);
346 char output_buffer
[20];
347 // Response code needs to be 200 to allow a pass through.
348 filter_context()->SetResponseCode(403);
349 // Meta refresh will only appear for html content
350 filter_context()->SetMimeType("text/html");
351 std::string
url_string("http://ignore.com");
352 filter_context()->SetURL(GURL(url_string
));
353 scoped_ptr
<Filter
> filter(Filter::Factory(filter_types
, *filter_context()));
355 // Supply enough data to force a pass-through mode, which means we have
356 // provided more than 9 characters that can't be a dictionary hash.
357 std::string
non_sdch_content("This is not SDCH");
359 char* input_buffer
= filter
->stream_buffer()->data();
360 int input_buffer_size
= filter
->stream_buffer_size();
362 EXPECT_LT(static_cast<int>(non_sdch_content
.size()),
364 memcpy(input_buffer
, non_sdch_content
.data(),
365 non_sdch_content
.size());
366 filter
->FlushStreamBuffer(non_sdch_content
.size());
368 // Try to read output.
369 int output_bytes_or_buffer_size
= sizeof(output_buffer
);
370 Filter::FilterStatus status
= filter
->ReadData(output_buffer
,
371 &output_bytes_or_buffer_size
);
373 // We should have read a long and complicated meta-refresh request.
374 EXPECT_EQ(sizeof(output_buffer
),
375 static_cast<size_t>(output_bytes_or_buffer_size
));
376 // Check at least the prefix of the return.
377 EXPECT_EQ(0, strncmp(output_buffer
,
378 "<head><META HTTP-EQUIV=\"Refresh\" CONTENT=\"0\"></head>",
379 sizeof(output_buffer
)));
380 EXPECT_EQ(Filter::FILTER_OK
, status
);
383 TEST_F(SdchFilterTest
, BasicBadDictionary
) {
384 std::vector
<Filter::FilterType
> filter_types
;
385 filter_types
.push_back(Filter::FILTER_TYPE_SDCH
);
386 char output_buffer
[20];
387 std::string
url_string("http://ignore.com");
388 filter_context()->SetURL(GURL(url_string
));
389 scoped_ptr
<Filter
> filter(Filter::Factory(filter_types
, *filter_context()));
391 // Supply bogus data (which doesn't yet specify a full dictionary hash).
392 // Dictionary hash is 8 characters followed by a null.
393 std::string
dictionary_hash_prefix("123");
395 char* input_buffer
= filter
->stream_buffer()->data();
396 int input_buffer_size
= filter
->stream_buffer_size();
398 EXPECT_LT(static_cast<int>(dictionary_hash_prefix
.size()),
400 memcpy(input_buffer
, dictionary_hash_prefix
.data(),
401 dictionary_hash_prefix
.size());
402 filter
->FlushStreamBuffer(dictionary_hash_prefix
.size());
404 // With less than a dictionary specifier, try to read output.
405 int output_bytes_or_buffer_size
= sizeof(output_buffer
);
406 Filter::FilterStatus status
= filter
->ReadData(output_buffer
,
407 &output_bytes_or_buffer_size
);
409 EXPECT_EQ(0, output_bytes_or_buffer_size
);
410 EXPECT_EQ(Filter::FILTER_NEED_MORE_DATA
, status
);
412 // Provide enough data to complete *a* hash, but it is bogus, and not in our
413 // list of dictionaries, so the filter should error out immediately.
414 std::string
dictionary_hash_postfix("4abcd\0", 6);
416 CHECK_LT(dictionary_hash_postfix
.size(),
417 static_cast<size_t>(input_buffer_size
));
418 memcpy(input_buffer
, dictionary_hash_postfix
.data(),
419 dictionary_hash_postfix
.size());
420 filter
->FlushStreamBuffer(dictionary_hash_postfix
.size());
422 // With a non-existant dictionary specifier, try to read output.
423 output_bytes_or_buffer_size
= sizeof(output_buffer
);
424 status
= filter
->ReadData(output_buffer
, &output_bytes_or_buffer_size
);
426 EXPECT_EQ(0, output_bytes_or_buffer_size
);
427 EXPECT_EQ(Filter::FILTER_ERROR
, status
);
429 EXPECT_EQ(SDCH_DOMAIN_BLACKLIST_INCLUDES_TARGET
,
430 sdch_manager_
->IsInSupportedDomain(GURL(url_string
)));
431 sdch_manager_
->ClearBlacklistings();
432 EXPECT_EQ(SDCH_OK
, sdch_manager_
->IsInSupportedDomain(GURL(url_string
)));
435 TEST_F(SdchFilterTest
, DictionaryAddOnce
) {
436 // Construct a valid SDCH dictionary from a VCDIFF dictionary.
437 const std::string kSampleDomain
= "sdchtest.com";
438 std::string
dictionary(NewSdchDictionary(kSampleDomain
));
440 std::string url_string
= "http://" + kSampleDomain
;
441 GURL
url(url_string
);
442 EXPECT_TRUE(AddSdchDictionary(dictionary
, url
));
444 // Check we can't add it twice.
445 EXPECT_FALSE(AddSdchDictionary(dictionary
, url
));
447 const std::string kSampleDomain2
= "sdchtest2.com";
449 // Don't test adding a second dictionary if our limits are tight.
450 if (SdchManager::kMaxDictionaryCount
> 1) {
451 // Construct a second SDCH dictionary from a VCDIFF dictionary.
452 std::string
dictionary2(NewSdchDictionary(kSampleDomain2
));
454 std::string url_string2
= "http://" + kSampleDomain2
;
455 GURL
url2(url_string2
);
456 EXPECT_TRUE(AddSdchDictionary(dictionary2
, url2
));
460 TEST_F(SdchFilterTest
, BasicDictionary
) {
461 // Construct a valid SDCH dictionary from a VCDIFF dictionary.
462 const std::string kSampleDomain
= "sdchtest.com";
463 std::string
dictionary(NewSdchDictionary(kSampleDomain
));
465 std::string url_string
= "http://" + kSampleDomain
;
467 GURL
url(url_string
);
468 EXPECT_TRUE(AddSdchDictionary(dictionary
, url
));
470 std::string
compressed(NewSdchCompressedData(dictionary
));
472 std::vector
<Filter::FilterType
> filter_types
;
473 filter_types
.push_back(Filter::FILTER_TYPE_SDCH
);
475 SetupFilterContextWithGURL(url
);
477 scoped_ptr
<Filter
> filter(Filter::Factory(filter_types
, *filter_context()));
479 size_t feed_block_size
= 100;
480 size_t output_block_size
= 100;
482 EXPECT_TRUE(FilterTestData(compressed
, feed_block_size
, output_block_size
,
483 filter
.get(), &output
));
484 EXPECT_EQ(output
, expanded_
);
486 // Decode with really small buffers (size 1) to check for edge effects.
487 filter
.reset(Filter::Factory(filter_types
, *filter_context()));
490 output_block_size
= 1;
492 EXPECT_TRUE(FilterTestData(compressed
, feed_block_size
, output_block_size
,
493 filter
.get(), &output
));
494 EXPECT_EQ(output
, expanded_
);
497 TEST_F(SdchFilterTest
, NoDecodeHttps
) {
498 // Construct a valid SDCH dictionary from a VCDIFF dictionary.
499 const std::string kSampleDomain
= "sdchtest.com";
500 std::string
dictionary(NewSdchDictionary(kSampleDomain
));
502 std::string url_string
= "http://" + kSampleDomain
;
504 GURL
url(url_string
);
505 EXPECT_TRUE(AddSdchDictionary(dictionary
, url
));
507 std::string
compressed(NewSdchCompressedData(dictionary
));
509 std::vector
<Filter::FilterType
> filter_types
;
510 filter_types
.push_back(Filter::FILTER_TYPE_SDCH
);
512 GURL
filter_context_gurl("https://" + kSampleDomain
);
513 SetupFilterContextWithGURL(GURL("https://" + kSampleDomain
));
514 scoped_ptr
<Filter
> filter(Filter::Factory(filter_types
, *filter_context()));
516 const size_t feed_block_size(100);
517 const size_t output_block_size(100);
520 EXPECT_FALSE(FilterTestData(compressed
, feed_block_size
, output_block_size
,
521 filter
.get(), &output
));
524 // Current failsafe TODO/hack refuses to decode any content that doesn't use
525 // http as the scheme (see use of DICTIONARY_SELECTED_FOR_NON_HTTP).
526 // The following tests this blockage. Note that blacklisting results, so we
527 // we need separate tests for each of these.
528 TEST_F(SdchFilterTest
, NoDecodeFtp
) {
529 // Construct a valid SDCH dictionary from a VCDIFF dictionary.
530 const std::string kSampleDomain
= "sdchtest.com";
531 std::string
dictionary(NewSdchDictionary(kSampleDomain
));
533 std::string url_string
= "http://" + kSampleDomain
;
535 GURL
url(url_string
);
536 EXPECT_TRUE(AddSdchDictionary(dictionary
, url
));
538 std::string
compressed(NewSdchCompressedData(dictionary
));
540 std::vector
<Filter::FilterType
> filter_types
;
541 filter_types
.push_back(Filter::FILTER_TYPE_SDCH
);
543 SetupFilterContextWithGURL(GURL("ftp://" + kSampleDomain
));
544 scoped_ptr
<Filter
> filter(Filter::Factory(filter_types
, *filter_context()));
546 const size_t feed_block_size(100);
547 const size_t output_block_size(100);
550 EXPECT_FALSE(FilterTestData(compressed
, feed_block_size
, output_block_size
,
551 filter
.get(), &output
));
554 TEST_F(SdchFilterTest
, NoDecodeFileColon
) {
555 // Construct a valid SDCH dictionary from a VCDIFF dictionary.
556 const std::string kSampleDomain
= "sdchtest.com";
557 std::string
dictionary(NewSdchDictionary(kSampleDomain
));
559 std::string url_string
= "http://" + kSampleDomain
;
561 GURL
url(url_string
);
562 EXPECT_TRUE(AddSdchDictionary(dictionary
, url
));
564 std::string
compressed(NewSdchCompressedData(dictionary
));
566 std::vector
<Filter::FilterType
> filter_types
;
567 filter_types
.push_back(Filter::FILTER_TYPE_SDCH
);
569 SetupFilterContextWithGURL(GURL("file://" + kSampleDomain
));
570 scoped_ptr
<Filter
> filter(Filter::Factory(filter_types
, *filter_context()));
572 const size_t feed_block_size(100);
573 const size_t output_block_size(100);
576 EXPECT_FALSE(FilterTestData(compressed
, feed_block_size
, output_block_size
,
577 filter
.get(), &output
));
580 TEST_F(SdchFilterTest
, NoDecodeAboutColon
) {
581 // Construct a valid SDCH dictionary from a VCDIFF dictionary.
582 const std::string kSampleDomain
= "sdchtest.com";
583 std::string
dictionary(NewSdchDictionary(kSampleDomain
));
585 std::string url_string
= "http://" + kSampleDomain
;
587 GURL
url(url_string
);
588 EXPECT_TRUE(AddSdchDictionary(dictionary
, url
));
590 std::string
compressed(NewSdchCompressedData(dictionary
));
592 std::vector
<Filter::FilterType
> filter_types
;
593 filter_types
.push_back(Filter::FILTER_TYPE_SDCH
);
595 SetupFilterContextWithGURL(GURL("about://" + kSampleDomain
));
596 scoped_ptr
<Filter
> filter(Filter::Factory(filter_types
, *filter_context()));
598 const size_t feed_block_size(100);
599 const size_t output_block_size(100);
602 EXPECT_FALSE(FilterTestData(compressed
, feed_block_size
, output_block_size
,
603 filter
.get(), &output
));
606 TEST_F(SdchFilterTest
, NoDecodeJavaScript
) {
607 // Construct a valid SDCH dictionary from a VCDIFF dictionary.
608 const std::string kSampleDomain
= "sdchtest.com";
609 std::string
dictionary(NewSdchDictionary(kSampleDomain
));
611 std::string url_string
= "http://" + kSampleDomain
;
613 GURL
url(url_string
);
614 EXPECT_TRUE(AddSdchDictionary(dictionary
, url
));
616 std::string
compressed(NewSdchCompressedData(dictionary
));
618 std::vector
<Filter::FilterType
> filter_types
;
619 filter_types
.push_back(Filter::FILTER_TYPE_SDCH
);
621 SetupFilterContextWithGURL(GURL("javascript://" + kSampleDomain
));
622 scoped_ptr
<Filter
> filter(Filter::Factory(filter_types
, *filter_context()));
624 const size_t feed_block_size(100);
625 const size_t output_block_size(100);
628 EXPECT_FALSE(FilterTestData(compressed
, feed_block_size
, output_block_size
,
629 filter
.get(), &output
));
632 TEST_F(SdchFilterTest
, CanStillDecodeHttp
) {
633 // Construct a valid SDCH dictionary from a VCDIFF dictionary.
634 const std::string kSampleDomain
= "sdchtest.com";
635 std::string
dictionary(NewSdchDictionary(kSampleDomain
));
637 std::string url_string
= "http://" + kSampleDomain
;
639 GURL
url(url_string
);
640 EXPECT_TRUE(AddSdchDictionary(dictionary
, url
));
642 std::string
compressed(NewSdchCompressedData(dictionary
));
644 std::vector
<Filter::FilterType
> filter_types
;
645 filter_types
.push_back(Filter::FILTER_TYPE_SDCH
);
647 SetupFilterContextWithGURL(GURL("http://" + kSampleDomain
));
648 scoped_ptr
<Filter
> filter(Filter::Factory(filter_types
, *filter_context()));
650 const size_t feed_block_size(100);
651 const size_t output_block_size(100);
654 EXPECT_TRUE(FilterTestData(compressed
, feed_block_size
, output_block_size
,
655 filter
.get(), &output
));
658 TEST_F(SdchFilterTest
, CrossDomainDictionaryUse
) {
659 // Construct a valid SDCH dictionary from a VCDIFF dictionary.
660 const std::string kSampleDomain
= "sdchtest.com";
661 std::string
dictionary(NewSdchDictionary(kSampleDomain
));
663 std::string url_string
= "http://" + kSampleDomain
;
665 GURL
url(url_string
);
666 EXPECT_TRUE(AddSdchDictionary(dictionary
, url
));
668 std::string
compressed(NewSdchCompressedData(dictionary
));
670 std::vector
<Filter::FilterType
> filter_types
;
671 filter_types
.push_back(Filter::FILTER_TYPE_SDCH
);
673 // Decode with content arriving from the "wrong" domain.
674 // This tests SdchManager::CanSet().
675 GURL
wrong_domain_url("http://www.wrongdomain.com");
676 SetupFilterContextWithGURL(wrong_domain_url
);
677 scoped_ptr
<Filter
> filter(Filter::Factory(filter_types
, *filter_context()));
679 size_t feed_block_size
= 100;
680 size_t output_block_size
= 100;
682 EXPECT_FALSE(FilterTestData(compressed
, feed_block_size
, output_block_size
,
683 filter
.get(), &output
));
684 EXPECT_EQ(output
.size(), 0u); // No output written.
686 EXPECT_EQ(SDCH_OK
, sdch_manager_
->IsInSupportedDomain(GURL(url_string
)));
687 EXPECT_EQ(SDCH_DOMAIN_BLACKLIST_INCLUDES_TARGET
,
688 sdch_manager_
->IsInSupportedDomain(wrong_domain_url
));
689 sdch_manager_
->ClearBlacklistings();
690 EXPECT_EQ(SDCH_OK
, sdch_manager_
->IsInSupportedDomain(wrong_domain_url
));
693 TEST_F(SdchFilterTest
, DictionaryPathValidation
) {
694 // Can't test path distinction between dictionaries if we aren't allowed
695 // more than one dictionary.
696 if (SdchManager::kMaxDictionaryCount
<= 1)
699 // Construct a valid SDCH dictionary from a VCDIFF dictionary.
700 const std::string kSampleDomain
= "sdchtest.com";
701 std::string
dictionary(NewSdchDictionary(kSampleDomain
));
703 std::string url_string
= "http://" + kSampleDomain
;
705 GURL
url(url_string
);
706 EXPECT_TRUE(AddSdchDictionary(dictionary
, url
));
708 // Create a dictionary with a path restriction, by prefixing dictionary.
709 const std::string
path("/special_path/bin");
710 std::string
dictionary_with_path("Path: " + path
+ "\n");
711 dictionary_with_path
.append(dictionary
);
712 GURL
url2(url_string
+ path
);
713 EXPECT_TRUE(AddSdchDictionary(dictionary_with_path
, url2
));
715 std::string
compressed_for_path(NewSdchCompressedData(dictionary_with_path
));
717 std::vector
<Filter::FilterType
> filter_types
;
718 filter_types
.push_back(Filter::FILTER_TYPE_SDCH
);
720 // Test decode the path data, arriving from a valid path.
721 SetupFilterContextWithGURL(GURL(url_string
+ path
));
722 scoped_ptr
<Filter
> filter(Filter::Factory(filter_types
, *filter_context()));
724 size_t feed_block_size
= 100;
725 size_t output_block_size
= 100;
728 EXPECT_TRUE(FilterTestData(compressed_for_path
, feed_block_size
,
729 output_block_size
, filter
.get(), &output
));
730 EXPECT_EQ(output
, expanded_
);
732 // Test decode the path data, arriving from a invalid path.
733 SetupFilterContextWithGURL(GURL(url_string
));
734 filter
.reset(Filter::Factory(filter_types
, *filter_context()));
736 feed_block_size
= 100;
737 output_block_size
= 100;
739 EXPECT_FALSE(FilterTestData(compressed_for_path
, feed_block_size
,
740 output_block_size
, filter
.get(), &output
));
741 EXPECT_EQ(output
.size(), 0u); // No output written.
743 EXPECT_EQ(SDCH_DOMAIN_BLACKLIST_INCLUDES_TARGET
,
744 sdch_manager_
->IsInSupportedDomain(GURL(url_string
)));
745 sdch_manager_
->ClearBlacklistings();
746 EXPECT_EQ(SDCH_OK
, sdch_manager_
->IsInSupportedDomain(GURL(url_string
)));
749 TEST_F(SdchFilterTest
, DictionaryPortValidation
) {
750 // Can't test port distinction between dictionaries if we aren't allowed
751 // more than one dictionary.
752 if (SdchManager::kMaxDictionaryCount
<= 1)
755 // Construct a valid SDCH dictionary from a VCDIFF dictionary.
756 const std::string kSampleDomain
= "sdchtest.com";
757 std::string
dictionary(NewSdchDictionary(kSampleDomain
));
759 std::string url_string
= "http://" + kSampleDomain
;
761 GURL
url(url_string
);
762 EXPECT_TRUE(AddSdchDictionary(dictionary
, url
));
764 // Create a dictionary with a port restriction, by prefixing old dictionary.
765 const std::string
port("502");
766 std::string
dictionary_with_port("Port: " + port
+ "\n");
767 dictionary_with_port
.append("Port: 80\n"); // Add default port.
768 dictionary_with_port
.append(dictionary
);
769 EXPECT_TRUE(AddSdchDictionary(dictionary_with_port
,
770 GURL(url_string
+ ":" + port
)));
772 std::string
compressed_for_port(NewSdchCompressedData(dictionary_with_port
));
774 std::vector
<Filter::FilterType
> filter_types
;
775 filter_types
.push_back(Filter::FILTER_TYPE_SDCH
);
777 // Test decode the port data, arriving from a valid port.
778 SetupFilterContextWithGURL(GURL(url_string
+ ":" + port
));
779 scoped_ptr
<Filter
> filter(Filter::Factory(filter_types
, *filter_context()));
781 size_t feed_block_size
= 100;
782 size_t output_block_size
= 100;
784 EXPECT_TRUE(FilterTestData(compressed_for_port
, feed_block_size
,
785 output_block_size
, filter
.get(), &output
));
786 EXPECT_EQ(output
, expanded_
);
788 // Test decode the port data, arriving from a valid (default) port.
789 SetupFilterContextWithGURL(GURL(url_string
)); // Default port.
790 filter
.reset(Filter::Factory(filter_types
, *filter_context()));
792 feed_block_size
= 100;
793 output_block_size
= 100;
795 EXPECT_TRUE(FilterTestData(compressed_for_port
, feed_block_size
,
796 output_block_size
, filter
.get(), &output
));
797 EXPECT_EQ(output
, expanded_
);
799 // Test decode the port data, arriving from a invalid port.
800 SetupFilterContextWithGURL(GURL(url_string
+ ":" + port
+ "1"));
801 filter
.reset(Filter::Factory(filter_types
, *filter_context()));
803 feed_block_size
= 100;
804 output_block_size
= 100;
806 EXPECT_FALSE(FilterTestData(compressed_for_port
, feed_block_size
,
807 output_block_size
, filter
.get(), &output
));
808 EXPECT_EQ(output
.size(), 0u); // No output written.
810 EXPECT_EQ(SDCH_DOMAIN_BLACKLIST_INCLUDES_TARGET
,
811 sdch_manager_
->IsInSupportedDomain(GURL(url_string
)));
812 sdch_manager_
->ClearBlacklistings();
813 EXPECT_EQ(SDCH_OK
, sdch_manager_
->IsInSupportedDomain(GURL(url_string
)));
816 //------------------------------------------------------------------------------
817 // Helper function to perform gzip compression of data.
819 static std::string
gzip_compress(const std::string
&input
) {
820 z_stream zlib_stream
;
821 memset(&zlib_stream
, 0, sizeof(zlib_stream
));
825 code
= deflateInit2(&zlib_stream
, Z_DEFAULT_COMPRESSION
, Z_DEFLATED
,
830 CHECK_EQ(Z_OK
, code
);
832 // Fill in zlib control block
833 zlib_stream
.next_in
= bit_cast
<Bytef
*>(input
.data());
834 zlib_stream
.avail_in
= input
.size();
836 // Assume we can compress into similar buffer (add 100 bytes to be sure).
837 size_t gzip_compressed_length
= zlib_stream
.avail_in
+ 100;
838 scoped_ptr
<char[]> gzip_compressed(new char[gzip_compressed_length
]);
839 zlib_stream
.next_out
= bit_cast
<Bytef
*>(gzip_compressed
.get());
840 zlib_stream
.avail_out
= gzip_compressed_length
;
842 // The GZIP header (see RFC 1952):
843 // +---+---+---+---+---+---+---+---+---+---+
844 // |ID1|ID2|CM |FLG| MTIME |XFL|OS |
845 // +---+---+---+---+---+---+---+---+---+---+
848 // CM \010 (compression method == DEFLATE)
849 // FLG \000 (special flags that we do not support)
850 // MTIME Unix format modification time (0 means not available)
851 // XFL 2-4? DEFLATE flags
852 // OS ???? Operating system indicator (255 means unknown)
854 // Header value we generate:
855 const char kGZipHeader
[] = { '\037', '\213', '\010', '\000', '\000',
856 '\000', '\000', '\000', '\002', '\377' };
857 CHECK_GT(zlib_stream
.avail_out
, sizeof(kGZipHeader
));
858 memcpy(zlib_stream
.next_out
, kGZipHeader
, sizeof(kGZipHeader
));
859 zlib_stream
.next_out
+= sizeof(kGZipHeader
);
860 zlib_stream
.avail_out
-= sizeof(kGZipHeader
);
863 code
= deflate(&zlib_stream
, Z_FINISH
);
864 gzip_compressed_length
-= zlib_stream
.avail_out
;
865 std::string
compressed(gzip_compressed
.get(), gzip_compressed_length
);
866 deflateEnd(&zlib_stream
);
870 //------------------------------------------------------------------------------
872 class SdchFilterChainingTest
{
874 static Filter
* Factory(const std::vector
<Filter::FilterType
>& types
,
875 const FilterContext
& context
, int size
) {
876 return Filter::FactoryForTests(types
, context
, size
);
880 // Test that filters can be cascaded (chained) so that the output of one filter
881 // is processed by the next one. This is most critical for SDCH, which is
882 // routinely followed by gzip (during encoding). The filter we'll test for will
883 // do the gzip decoding first, and then decode the SDCH content.
884 TEST_F(SdchFilterTest
, FilterChaining
) {
885 // Construct a valid SDCH dictionary from a VCDIFF dictionary.
886 const std::string kSampleDomain
= "sdchtest.com";
887 std::string
dictionary(NewSdchDictionary(kSampleDomain
));
889 std::string url_string
= "http://" + kSampleDomain
;
891 GURL
url(url_string
);
892 EXPECT_TRUE(AddSdchDictionary(dictionary
, url
));
894 std::string
sdch_compressed(NewSdchCompressedData(dictionary
));
896 // Use Gzip to compress the sdch sdch_compressed data.
897 std::string gzip_compressed_sdch
= gzip_compress(sdch_compressed
);
899 // Construct a chained filter.
900 std::vector
<Filter::FilterType
> filter_types
;
901 filter_types
.push_back(Filter::FILTER_TYPE_SDCH
);
902 filter_types
.push_back(Filter::FILTER_TYPE_GZIP
);
904 // First try with a large buffer (larger than test input, or compressed data).
905 const size_t kLargeInputBufferSize(1000); // Used internally in filters.
906 CHECK_GT(kLargeInputBufferSize
, gzip_compressed_sdch
.size());
907 CHECK_GT(kLargeInputBufferSize
, sdch_compressed
.size());
908 CHECK_GT(kLargeInputBufferSize
, expanded_
.size());
909 SetupFilterContextWithGURL(url
);
910 scoped_ptr
<Filter
> filter(
911 SdchFilterChainingTest::Factory(filter_types
, *filter_context(),
912 kLargeInputBufferSize
));
913 EXPECT_EQ(static_cast<int>(kLargeInputBufferSize
),
914 filter
->stream_buffer_size());
916 // Verify that chained filter is waiting for data.
917 char tiny_output_buffer
[10];
918 int tiny_output_size
= sizeof(tiny_output_buffer
);
919 EXPECT_EQ(Filter::FILTER_NEED_MORE_DATA
,
920 filter
->ReadData(tiny_output_buffer
, &tiny_output_size
));
922 // Make chain process all data.
923 size_t feed_block_size
= kLargeInputBufferSize
;
924 size_t output_block_size
= kLargeInputBufferSize
;
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 mid-sized internal buffer size.
931 const size_t kMidSizedInputBufferSize(100);
932 // Buffer should be big enough to swallow whole gzip content.
933 CHECK_GT(kMidSizedInputBufferSize
, gzip_compressed_sdch
.size());
934 // Buffer should be small enough that entire SDCH content can't fit.
935 // We'll go even further, and force the chain to flush the buffer between the
936 // two filters more than once (that is why we multiply by 2).
937 CHECK_LT(kMidSizedInputBufferSize
* 2, sdch_compressed
.size());
938 filter_context()->SetURL(url
);
940 SdchFilterChainingTest::Factory(filter_types
, *filter_context(),
941 kMidSizedInputBufferSize
));
942 EXPECT_EQ(static_cast<int>(kMidSizedInputBufferSize
),
943 filter
->stream_buffer_size());
945 feed_block_size
= kMidSizedInputBufferSize
;
946 output_block_size
= kMidSizedInputBufferSize
;
948 EXPECT_TRUE(FilterTestData(gzip_compressed_sdch
, feed_block_size
,
949 output_block_size
, filter
.get(), &output
));
950 EXPECT_EQ(output
, expanded_
);
952 // Next try with a tiny input and output buffer to cover edge effects.
953 filter
.reset(SdchFilterChainingTest::Factory(filter_types
, *filter_context(),
954 kLargeInputBufferSize
));
955 EXPECT_EQ(static_cast<int>(kLargeInputBufferSize
),
956 filter
->stream_buffer_size());
959 output_block_size
= 1;
961 EXPECT_TRUE(FilterTestData(gzip_compressed_sdch
, feed_block_size
,
962 output_block_size
, filter
.get(), &output
));
963 EXPECT_EQ(output
, expanded_
);
966 TEST_F(SdchFilterTest
, DefaultGzipIfSdch
) {
967 // Construct a valid SDCH dictionary from a VCDIFF dictionary.
968 const std::string kSampleDomain
= "sdchtest.com";
969 std::string
dictionary(NewSdchDictionary(kSampleDomain
));
971 std::string url_string
= "http://" + kSampleDomain
;
973 GURL
url(url_string
);
974 EXPECT_TRUE(AddSdchDictionary(dictionary
, url
));
976 std::string
sdch_compressed(NewSdchCompressedData(dictionary
));
978 // Use Gzip to compress the sdch sdch_compressed data.
979 std::string gzip_compressed_sdch
= gzip_compress(sdch_compressed
);
981 // Only claim to have sdch content, but really use the gzipped sdch content.
982 // System should automatically add the missing (optional) gzip.
983 std::vector
<Filter::FilterType
> filter_types
;
984 filter_types
.push_back(Filter::FILTER_TYPE_SDCH
);
986 filter_context()->SetMimeType("anything/mime");
987 SetupFilterContextWithGURL(url
);
989 Filter::FixupEncodingTypes(*filter_context(), &filter_types
);
990 ASSERT_EQ(filter_types
.size(), 2u);
991 EXPECT_EQ(filter_types
[0], Filter::FILTER_TYPE_SDCH
);
992 EXPECT_EQ(filter_types
[1], Filter::FILTER_TYPE_GZIP_HELPING_SDCH
);
994 // First try with a large buffer (larger than test input, or compressed data).
995 scoped_ptr
<Filter
> filter(Filter::Factory(filter_types
, *filter_context()));
997 // Verify that chained filter is waiting for data.
998 char tiny_output_buffer
[10];
999 int tiny_output_size
= sizeof(tiny_output_buffer
);
1000 EXPECT_EQ(Filter::FILTER_NEED_MORE_DATA
,
1001 filter
->ReadData(tiny_output_buffer
, &tiny_output_size
));
1003 size_t feed_block_size
= 100;
1004 size_t output_block_size
= 100;
1006 EXPECT_TRUE(FilterTestData(gzip_compressed_sdch
, feed_block_size
,
1007 output_block_size
, filter
.get(), &output
));
1008 EXPECT_EQ(output
, expanded_
);
1010 // Next try with a tiny buffer to cover edge effects.
1011 filter
.reset(Filter::Factory(filter_types
, *filter_context()));
1013 feed_block_size
= 1;
1014 output_block_size
= 1;
1016 EXPECT_TRUE(FilterTestData(gzip_compressed_sdch
, feed_block_size
,
1017 output_block_size
, filter
.get(), &output
));
1018 EXPECT_EQ(output
, expanded_
);
1021 TEST_F(SdchFilterTest
, AcceptGzipSdchIfGzip
) {
1022 // Construct a valid SDCH dictionary from a VCDIFF dictionary.
1023 const std::string kSampleDomain
= "sdchtest.com";
1024 std::string
dictionary(NewSdchDictionary(kSampleDomain
));
1026 std::string url_string
= "http://" + kSampleDomain
;
1028 GURL
url(url_string
);
1029 EXPECT_TRUE(AddSdchDictionary(dictionary
, url
));
1031 std::string
sdch_compressed(NewSdchCompressedData(dictionary
));
1033 // Use Gzip to compress the sdch sdch_compressed data.
1034 std::string gzip_compressed_sdch
= gzip_compress(sdch_compressed
);
1036 // Some proxies strip the content encoding statement down to a mere gzip, but
1037 // pass through the original content (with full sdch,gzip encoding).
1038 // Only claim to have gzip content, but really use the gzipped sdch content.
1039 // System should automatically add the missing (optional) sdch.
1040 std::vector
<Filter::FilterType
> filter_types
;
1041 filter_types
.push_back(Filter::FILTER_TYPE_GZIP
);
1043 filter_context()->SetMimeType("anything/mime");
1044 SetupFilterContextWithGURL(url
);
1045 Filter::FixupEncodingTypes(*filter_context(), &filter_types
);
1046 ASSERT_EQ(filter_types
.size(), 3u);
1047 EXPECT_EQ(filter_types
[0], Filter::FILTER_TYPE_SDCH_POSSIBLE
);
1048 EXPECT_EQ(filter_types
[1], Filter::FILTER_TYPE_GZIP_HELPING_SDCH
);
1049 EXPECT_EQ(filter_types
[2], Filter::FILTER_TYPE_GZIP
);
1051 // First try with a large buffer (larger than test input, or compressed data).
1052 scoped_ptr
<Filter
> filter(Filter::Factory(filter_types
, *filter_context()));
1054 // Verify that chained filter is waiting for data.
1055 char tiny_output_buffer
[10];
1056 int tiny_output_size
= sizeof(tiny_output_buffer
);
1057 EXPECT_EQ(Filter::FILTER_NEED_MORE_DATA
,
1058 filter
->ReadData(tiny_output_buffer
, &tiny_output_size
));
1060 size_t feed_block_size
= 100;
1061 size_t output_block_size
= 100;
1063 EXPECT_TRUE(FilterTestData(gzip_compressed_sdch
, feed_block_size
,
1064 output_block_size
, filter
.get(), &output
));
1065 EXPECT_EQ(output
, expanded_
);
1067 // Next try with a tiny buffer to cover edge effects.
1068 filter
.reset(Filter::Factory(filter_types
, *filter_context()));
1070 feed_block_size
= 1;
1071 output_block_size
= 1;
1073 EXPECT_TRUE(FilterTestData(gzip_compressed_sdch
, feed_block_size
,
1074 output_block_size
, filter
.get(), &output
));
1075 EXPECT_EQ(output
, expanded_
);
1078 TEST_F(SdchFilterTest
, DefaultSdchGzipIfEmpty
) {
1079 // Construct a valid SDCH dictionary from a VCDIFF dictionary.
1080 const std::string kSampleDomain
= "sdchtest.com";
1081 std::string
dictionary(NewSdchDictionary(kSampleDomain
));
1083 std::string url_string
= "http://" + kSampleDomain
;
1085 GURL
url(url_string
);
1086 EXPECT_TRUE(AddSdchDictionary(dictionary
, url
));
1088 std::string
sdch_compressed(NewSdchCompressedData(dictionary
));
1090 // Use Gzip to compress the sdch sdch_compressed data.
1091 std::string gzip_compressed_sdch
= gzip_compress(sdch_compressed
);
1093 // Only claim to have non-encoded content, but really use the gzipped sdch
1095 // System should automatically add the missing (optional) sdch,gzip.
1096 std::vector
<Filter::FilterType
> filter_types
;
1098 filter_context()->SetMimeType("anything/mime");
1099 SetupFilterContextWithGURL(url
);
1100 Filter::FixupEncodingTypes(*filter_context(), &filter_types
);
1101 ASSERT_EQ(filter_types
.size(), 2u);
1102 EXPECT_EQ(filter_types
[0], Filter::FILTER_TYPE_SDCH_POSSIBLE
);
1103 EXPECT_EQ(filter_types
[1], Filter::FILTER_TYPE_GZIP_HELPING_SDCH
);
1105 // First try with a large buffer (larger than test input, or compressed data).
1106 scoped_ptr
<Filter
> filter(Filter::Factory(filter_types
, *filter_context()));
1108 // Verify that chained filter is waiting for data.
1109 char tiny_output_buffer
[10];
1110 int tiny_output_size
= sizeof(tiny_output_buffer
);
1111 EXPECT_EQ(Filter::FILTER_NEED_MORE_DATA
,
1112 filter
->ReadData(tiny_output_buffer
, &tiny_output_size
));
1114 size_t feed_block_size
= 100;
1115 size_t output_block_size
= 100;
1117 EXPECT_TRUE(FilterTestData(gzip_compressed_sdch
, feed_block_size
,
1118 output_block_size
, filter
.get(), &output
));
1119 EXPECT_EQ(output
, expanded_
);
1121 // Next try with a tiny buffer to cover edge effects.
1122 filter
.reset(Filter::Factory(filter_types
, *filter_context()));
1124 feed_block_size
= 1;
1125 output_block_size
= 1;
1127 EXPECT_TRUE(FilterTestData(gzip_compressed_sdch
, feed_block_size
,
1128 output_block_size
, filter
.get(), &output
));
1129 EXPECT_EQ(output
, expanded_
);
1132 TEST_F(SdchFilterTest
, AcceptGzipGzipSdchIfGzip
) {
1133 // Construct a valid SDCH dictionary from a VCDIFF dictionary.
1134 const std::string kSampleDomain
= "sdchtest.com";
1135 std::string
dictionary(NewSdchDictionary(kSampleDomain
));
1137 std::string url_string
= "http://" + kSampleDomain
;
1139 GURL
url(url_string
);
1140 EXPECT_TRUE(AddSdchDictionary(dictionary
, url
));
1142 std::string
sdch_compressed(NewSdchCompressedData(dictionary
));
1144 // Vodaphone (UK) Mobile Broadband provides double gzipped sdch with a content
1145 // encoding of merely gzip (apparently, only listing the extra level of
1146 // wrapper compression they added, but discarding the actual content encoding.
1147 // Use Gzip to double compress the sdch sdch_compressed data.
1148 std::string double_gzip_compressed_sdch
= gzip_compress(gzip_compress(
1151 // Only claim to have gzip content, but really use the double gzipped sdch
1153 // System should automatically add the missing (optional) sdch, gzip decoders.
1154 std::vector
<Filter::FilterType
> filter_types
;
1155 filter_types
.push_back(Filter::FILTER_TYPE_GZIP
);
1157 filter_context()->SetMimeType("anything/mime");
1158 SetupFilterContextWithGURL(url
);
1159 Filter::FixupEncodingTypes(*filter_context(), &filter_types
);
1160 ASSERT_EQ(filter_types
.size(), 3u);
1161 EXPECT_EQ(filter_types
[0], Filter::FILTER_TYPE_SDCH_POSSIBLE
);
1162 EXPECT_EQ(filter_types
[1], Filter::FILTER_TYPE_GZIP_HELPING_SDCH
);
1163 EXPECT_EQ(filter_types
[2], Filter::FILTER_TYPE_GZIP
);
1165 // First try with a large buffer (larger than test input, or compressed data).
1166 scoped_ptr
<Filter
> filter(Filter::Factory(filter_types
, *filter_context()));
1168 // Verify that chained filter is waiting for data.
1169 char tiny_output_buffer
[10];
1170 int tiny_output_size
= sizeof(tiny_output_buffer
);
1171 EXPECT_EQ(Filter::FILTER_NEED_MORE_DATA
,
1172 filter
->ReadData(tiny_output_buffer
, &tiny_output_size
));
1174 size_t feed_block_size
= 100;
1175 size_t output_block_size
= 100;
1177 EXPECT_TRUE(FilterTestData(double_gzip_compressed_sdch
, feed_block_size
,
1178 output_block_size
, filter
.get(), &output
));
1179 EXPECT_EQ(output
, expanded_
);
1181 // Next try with a tiny buffer to cover edge effects.
1182 filter
.reset(Filter::Factory(filter_types
, *filter_context()));
1184 feed_block_size
= 1;
1185 output_block_size
= 1;
1187 EXPECT_TRUE(FilterTestData(double_gzip_compressed_sdch
, feed_block_size
,
1188 output_block_size
, filter
.get(), &output
));
1189 EXPECT_EQ(output
, expanded_
);
1192 // Test to make sure we decode properly with an unexpected dictionary.
1193 TEST_F(SdchFilterTest
, UnexpectedDictionary
) {
1194 // Setup a dictionary, add it to the filter context, and create a filter
1195 // based on that dictionary.
1196 const std::string kSampleDomain
= "sdchtest.com";
1197 std::string
dictionary(NewSdchDictionary(kSampleDomain
));
1198 std::string url_string
= "http://" + kSampleDomain
;
1199 GURL
url(url_string
);
1200 EXPECT_TRUE(AddSdchDictionary(dictionary
, url
));
1202 SetupFilterContextWithGURL(url
);
1204 std::vector
<Filter::FilterType
> filter_types
;
1205 filter_types
.push_back(Filter::FILTER_TYPE_SDCH
);
1206 scoped_ptr
<Filter
> filter(Filter::Factory(filter_types
, *filter_context()));
1208 // Setup another dictionary, expired. Don't add it to the filter context.
1209 // Delete stored dictionaries first to handle platforms which only
1210 // have room for a single dictionary.
1211 sdch_manager_
->ClearData();
1212 std::string
expired_dictionary(NewSdchExpiredDictionary(kSampleDomain
));
1214 // Don't use the Helper function since its insertion check is indeterminate
1215 // for a Max-Age: 0 dictionary.
1216 sdch_manager_
->AddSdchDictionary(expired_dictionary
, url
);
1218 std::string client_hash
;
1219 std::string server_hash
;
1220 SdchManager::GenerateHash(expired_dictionary
, &client_hash
, &server_hash
);
1222 // Make sure Max-Age: 0 shows up as expired.
1223 scoped_ptr
<base::SimpleTestClock
> clock(new base::SimpleTestClock
);
1224 clock
->SetNow(base::Time::Now());
1225 clock
->Advance(base::TimeDelta::FromMinutes(5));
1226 SdchProblemCode problem_code
;
1227 scoped_ptr
<SdchManager::DictionarySet
> hash_set(
1228 sdch_manager_
->GetDictionarySetByHash(
1229 url
, server_hash
, &problem_code
).Pass());
1230 ASSERT_TRUE(hash_set
);
1231 ASSERT_EQ(SDCH_OK
, problem_code
);
1233 const_cast<SdchManager::Dictionary
*>(
1234 hash_set
->GetDictionary(server_hash
))->SetClockForTesting(
1237 // Encode output with the second dictionary.
1238 std::string
sdch_compressed(NewSdchCompressedData(expired_dictionary
));
1240 // See if the filter decodes it.
1242 EXPECT_TRUE(FilterTestData(sdch_compressed
, 100, 100, filter
.get(), &output
));
1243 EXPECT_EQ(expanded_
, output
);