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/histogram_tester.h"
14 #include "base/test/simple_test_clock.h"
15 #include "net/base/io_buffer.h"
16 #include "net/base/sdch_observer.h"
17 #include "net/filter/mock_filter_context.h"
18 #include "net/filter/sdch_filter.h"
19 #include "net/url_request/url_request_context.h"
20 #include "net/url_request/url_request_http_job.h"
21 #include "testing/gtest/include/gtest/gtest.h"
22 #include "third_party/zlib/zlib.h"
26 //------------------------------------------------------------------------------
27 // Provide sample data and compression results with a sample VCDIFF dictionary.
28 // Note an SDCH dictionary has extra meta-data before the VCDIFF dictionary.
29 static const char kTestVcdiffDictionary
[] = "DictionaryFor"
30 "SdchCompression1SdchCompression2SdchCompression3SdchCompression\n";
31 // Pre-compression test data. Note that we pad with a lot of highly gzip
32 // compressible content to help to exercise the chaining pipeline. That is why
33 // there are a PILE of zeros at the start and end.
34 // This will ensure that gzip compressed data can be fed to the chain in one
35 // gulp, but (with careful selection of intermediate buffers) that it takes
36 // several sdch buffers worth of data to satisfy the sdch filter. See detailed
37 // CHECK() calls in FilterChaining test for specifics.
38 static const char kTestData
[] = "0000000000000000000000000000000000000000000000"
39 "0000000000000000000000000000TestData "
40 "SdchCompression1SdchCompression2SdchCompression3SdchCompression"
41 "00000000000000000000000000000000000000000000000000000000000000000000000000"
42 "000000000000000000000000000000000000000\n";
44 // Note SDCH compressed data will include a reference to the SDCH dictionary.
45 static const char kSdchCompressedTestData
[] =
46 "\326\303\304\0\0\001M\0\201S\202\004\0\201E\006\001"
47 "00000000000000000000000000000000000000000000000000000000000000000000000000"
48 "TestData 00000000000000000000000000000000000000000000000000000000000000000"
49 "000000000000000000000000000000000000000000000000\n\001S\023\077\001r\r";
51 //------------------------------------------------------------------------------
53 class SdchFilterTest
: public testing::Test
{
56 : test_vcdiff_dictionary_(kTestVcdiffDictionary
,
57 sizeof(kTestVcdiffDictionary
) - 1),
58 vcdiff_compressed_data_(kSdchCompressedTestData
,
59 sizeof(kSdchCompressedTestData
) - 1),
60 expanded_(kTestData
, sizeof(kTestData
) - 1),
61 sdch_manager_(new SdchManager
),
62 filter_context_(new MockFilterContext
) {
63 URLRequestContext
* url_request_context
=
64 filter_context_
->GetModifiableURLRequestContext();
66 url_request_context
->set_sdch_manager(sdch_manager_
.get());
69 // Attempt to add a dictionary to the manager and probe for success or
71 bool AddSdchDictionary(const std::string
& dictionary_text
,
73 return sdch_manager_
->AddSdchDictionary(dictionary_text
, gurl
, nullptr) ==
77 MockFilterContext
* filter_context() { return filter_context_
.get(); }
79 // Sets both the GURL and the SDCH response for a filter context.
80 void SetupFilterContextWithGURL(GURL url
) {
81 filter_context_
->SetURL(url
);
82 filter_context_
->SetSdchResponse(
83 sdch_manager_
->GetDictionarySet(url
).Pass());
86 std::string
NewSdchCompressedData(const std::string dictionary
) {
87 std::string client_hash
;
88 std::string server_hash
;
89 SdchManager::GenerateHash(dictionary
, &client_hash
, &server_hash
);
91 // Build compressed data that refers to our dictionary.
92 std::string
compressed(server_hash
);
93 compressed
.append("\0", 1);
94 compressed
.append(vcdiff_compressed_data_
);
98 const std::string test_vcdiff_dictionary_
;
99 const std::string vcdiff_compressed_data_
;
100 const std::string expanded_
; // Desired final, decompressed data.
102 scoped_ptr
<SdchManager
> sdch_manager_
;
103 scoped_ptr
<MockFilterContext
> filter_context_
;
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");
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)
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 static std::string
NewSdchExpiredDictionary(const std::string
& domain
) {
170 std::string dictionary
;
171 if (!domain
.empty()) {
172 dictionary
.append("Domain: ");
173 dictionary
.append(domain
);
174 dictionary
.append("\n");
176 dictionary
.append("Max-Age: 0\n");
177 dictionary
.append("\n");
178 dictionary
.append(kTestVcdiffDictionary
, sizeof(kTestVcdiffDictionary
) - 1);
182 TEST_F(SdchFilterTest
, EmptyInputOk
) {
183 std::vector
<Filter::FilterType
> filter_types
;
184 filter_types
.push_back(Filter::FILTER_TYPE_SDCH
);
185 char output_buffer
[20];
186 std::string
url_string("http://ignore.com");
187 filter_context()->SetURL(GURL(url_string
));
188 scoped_ptr
<Filter
> filter(Filter::Factory(filter_types
, *filter_context()));
190 // With no input data, try to read output.
191 int output_bytes_or_buffer_size
= sizeof(output_buffer
);
192 Filter::FilterStatus status
= filter
->ReadData(output_buffer
,
193 &output_bytes_or_buffer_size
);
195 EXPECT_EQ(0, output_bytes_or_buffer_size
);
196 EXPECT_EQ(Filter::FILTER_NEED_MORE_DATA
, status
);
199 // Make sure that the filter context has everything that might be
200 // nuked from it during URLRequest teardown before the SdchFilter
202 TEST_F(SdchFilterTest
, SparseContextOk
) {
203 std::vector
<Filter::FilterType
> filter_types
;
204 filter_types
.push_back(Filter::FILTER_TYPE_SDCH
);
205 char output_buffer
[20];
206 std::string
url_string("http://ignore.com");
207 filter_context()->SetURL(GURL(url_string
));
208 scoped_ptr
<Filter
> filter(Filter::Factory(filter_types
, *filter_context()));
210 // With no input data, try to read output.
211 int output_bytes_or_buffer_size
= sizeof(output_buffer
);
212 Filter::FilterStatus status
= filter
->ReadData(output_buffer
,
213 &output_bytes_or_buffer_size
);
215 EXPECT_EQ(0, output_bytes_or_buffer_size
);
216 EXPECT_EQ(Filter::FILTER_NEED_MORE_DATA
, status
);
218 // Partially tear down context. Anything that goes through request()
219 // without checking it for null in the URLRequestJob::HttpFilterContext
220 // implementation is suspect. Everything that does check it for null should
221 // return null. This is to test for incorrectly relying on filter_context()
222 // from the SdchFilter destructor.
223 filter_context()->NukeUnstableInterfaces();
226 TEST_F(SdchFilterTest
, PassThroughWhenTentative
) {
227 std::vector
<Filter::FilterType
> filter_types
;
228 // Selective a tentative filter (which can fall back to pass through).
229 filter_types
.push_back(Filter::FILTER_TYPE_GZIP_HELPING_SDCH
);
230 char output_buffer
[20];
231 // Response code needs to be 200 to allow a pass through.
232 filter_context()->SetResponseCode(200);
233 std::string
url_string("http://ignore.com");
234 filter_context()->SetURL(GURL(url_string
));
235 scoped_ptr
<Filter
> filter(Filter::Factory(filter_types
, *filter_context()));
237 // Supply enough data to force a pass-through mode..
238 std::string
non_gzip_content("not GZIPed data");
240 char* input_buffer
= filter
->stream_buffer()->data();
241 int input_buffer_size
= filter
->stream_buffer_size();
243 EXPECT_LT(static_cast<int>(non_gzip_content
.size()),
245 memcpy(input_buffer
, non_gzip_content
.data(),
246 non_gzip_content
.size());
247 filter
->FlushStreamBuffer(non_gzip_content
.size());
249 // Try to read output.
250 int output_bytes_or_buffer_size
= sizeof(output_buffer
);
251 Filter::FilterStatus status
= filter
->ReadData(output_buffer
,
252 &output_bytes_or_buffer_size
);
254 EXPECT_EQ(non_gzip_content
.size(),
255 static_cast<size_t>(output_bytes_or_buffer_size
));
256 ASSERT_GT(sizeof(output_buffer
),
257 static_cast<size_t>(output_bytes_or_buffer_size
));
258 output_buffer
[output_bytes_or_buffer_size
] = '\0';
259 EXPECT_TRUE(non_gzip_content
== output_buffer
);
260 EXPECT_EQ(Filter::FILTER_NEED_MORE_DATA
, status
);
263 TEST_F(SdchFilterTest
, RefreshBadReturnCode
) {
264 std::vector
<Filter::FilterType
> filter_types
;
265 // Selective a tentative filter (which can fall back to pass through).
266 filter_types
.push_back(Filter::FILTER_TYPE_SDCH_POSSIBLE
);
267 char output_buffer
[20];
268 // Response code needs to be 200 to allow a pass through.
269 filter_context()->SetResponseCode(403);
270 // Meta refresh will only appear for html content
271 filter_context()->SetMimeType("text/html");
272 std::string
url_string("http://ignore.com");
273 filter_context()->SetURL(GURL(url_string
));
274 scoped_ptr
<Filter
> filter(Filter::Factory(filter_types
, *filter_context()));
276 // Supply enough data to force a pass-through mode, which means we have
277 // provided more than 9 characters that can't be a dictionary hash.
278 std::string
non_sdch_content("This is not SDCH");
280 char* input_buffer
= filter
->stream_buffer()->data();
281 int input_buffer_size
= filter
->stream_buffer_size();
283 EXPECT_LT(static_cast<int>(non_sdch_content
.size()),
285 memcpy(input_buffer
, non_sdch_content
.data(),
286 non_sdch_content
.size());
287 filter
->FlushStreamBuffer(non_sdch_content
.size());
289 // Try to read output.
290 int output_bytes_or_buffer_size
= sizeof(output_buffer
);
291 Filter::FilterStatus status
= filter
->ReadData(output_buffer
,
292 &output_bytes_or_buffer_size
);
294 // We should have read a long and complicated meta-refresh request.
295 EXPECT_TRUE(sizeof(output_buffer
) == output_bytes_or_buffer_size
);
296 // Check at least the prefix of the return.
297 EXPECT_EQ(0, strncmp(output_buffer
,
298 "<head><META HTTP-EQUIV=\"Refresh\" CONTENT=\"0\"></head>",
299 sizeof(output_buffer
)));
300 EXPECT_EQ(Filter::FILTER_OK
, status
);
303 TEST_F(SdchFilterTest
, ErrorOnBadReturnCode
) {
304 std::vector
<Filter::FilterType
> filter_types
;
305 // Selective a tentative filter (which can fall back to pass through).
306 filter_types
.push_back(Filter::FILTER_TYPE_SDCH_POSSIBLE
);
307 char output_buffer
[20];
308 // Response code needs to be 200 to allow a pass through.
309 filter_context()->SetResponseCode(403);
310 // Meta refresh will only appear for html content, so set to something else
311 // to induce an error (we can't meta refresh).
312 filter_context()->SetMimeType("anything");
313 std::string
url_string("http://ignore.com");
314 filter_context()->SetURL(GURL(url_string
));
315 scoped_ptr
<Filter
> filter(Filter::Factory(filter_types
, *filter_context()));
317 // Supply enough data to force a pass-through mode, which means we have
318 // provided more than 9 characters that can't be a dictionary hash.
319 std::string
non_sdch_content("This is not SDCH");
321 char* input_buffer
= filter
->stream_buffer()->data();
322 int input_buffer_size
= filter
->stream_buffer_size();
324 EXPECT_LT(static_cast<int>(non_sdch_content
.size()),
326 memcpy(input_buffer
, non_sdch_content
.data(),
327 non_sdch_content
.size());
328 filter
->FlushStreamBuffer(non_sdch_content
.size());
330 // Try to read output.
331 int output_bytes_or_buffer_size
= sizeof(output_buffer
);
332 Filter::FilterStatus status
= filter
->ReadData(output_buffer
,
333 &output_bytes_or_buffer_size
);
335 EXPECT_EQ(0, output_bytes_or_buffer_size
);
336 EXPECT_EQ(Filter::FILTER_ERROR
, status
);
339 TEST_F(SdchFilterTest
, ErrorOnBadReturnCodeWithHtml
) {
340 std::vector
<Filter::FilterType
> filter_types
;
341 // Selective a tentative filter (which can fall back to pass through).
342 filter_types
.push_back(Filter::FILTER_TYPE_SDCH_POSSIBLE
);
343 char output_buffer
[20];
344 // Response code needs to be 200 to allow a pass through.
345 filter_context()->SetResponseCode(403);
346 // Meta refresh will only appear for html content
347 filter_context()->SetMimeType("text/html");
348 std::string
url_string("http://ignore.com");
349 filter_context()->SetURL(GURL(url_string
));
350 scoped_ptr
<Filter
> filter(Filter::Factory(filter_types
, *filter_context()));
352 // Supply enough data to force a pass-through mode, which means we have
353 // provided more than 9 characters that can't be a dictionary hash.
354 std::string
non_sdch_content("This is not SDCH");
356 char* input_buffer
= filter
->stream_buffer()->data();
357 int input_buffer_size
= filter
->stream_buffer_size();
359 EXPECT_LT(static_cast<int>(non_sdch_content
.size()),
361 memcpy(input_buffer
, non_sdch_content
.data(),
362 non_sdch_content
.size());
363 filter
->FlushStreamBuffer(non_sdch_content
.size());
365 // Try to read output.
366 int output_bytes_or_buffer_size
= sizeof(output_buffer
);
367 Filter::FilterStatus status
= filter
->ReadData(output_buffer
,
368 &output_bytes_or_buffer_size
);
370 // We should have read a long and complicated meta-refresh request.
371 EXPECT_EQ(sizeof(output_buffer
),
372 static_cast<size_t>(output_bytes_or_buffer_size
));
373 // Check at least the prefix of the return.
374 EXPECT_EQ(0, strncmp(output_buffer
,
375 "<head><META HTTP-EQUIV=\"Refresh\" CONTENT=\"0\"></head>",
376 sizeof(output_buffer
)));
377 EXPECT_EQ(Filter::FILTER_OK
, status
);
380 TEST_F(SdchFilterTest
, BasicBadDictionary
) {
381 std::vector
<Filter::FilterType
> filter_types
;
382 filter_types
.push_back(Filter::FILTER_TYPE_SDCH
);
383 char output_buffer
[20];
384 std::string
url_string("http://ignore.com");
385 filter_context()->SetURL(GURL(url_string
));
386 scoped_ptr
<Filter
> filter(Filter::Factory(filter_types
, *filter_context()));
388 // Supply bogus data (which doesn't yet specify a full dictionary hash).
389 // Dictionary hash is 8 characters followed by a null.
390 std::string
dictionary_hash_prefix("123");
392 char* input_buffer
= filter
->stream_buffer()->data();
393 int input_buffer_size
= filter
->stream_buffer_size();
395 EXPECT_LT(static_cast<int>(dictionary_hash_prefix
.size()),
397 memcpy(input_buffer
, dictionary_hash_prefix
.data(),
398 dictionary_hash_prefix
.size());
399 filter
->FlushStreamBuffer(dictionary_hash_prefix
.size());
401 // With less than a dictionary specifier, try to read output.
402 int output_bytes_or_buffer_size
= sizeof(output_buffer
);
403 Filter::FilterStatus status
= filter
->ReadData(output_buffer
,
404 &output_bytes_or_buffer_size
);
406 EXPECT_EQ(0, output_bytes_or_buffer_size
);
407 EXPECT_EQ(Filter::FILTER_NEED_MORE_DATA
, status
);
409 // Provide enough data to complete *a* hash, but it is bogus, and not in our
410 // list of dictionaries, so the filter should error out immediately.
411 std::string
dictionary_hash_postfix("4abcd\0", 6);
413 CHECK_LT(dictionary_hash_postfix
.size(),
414 static_cast<size_t>(input_buffer_size
));
415 memcpy(input_buffer
, dictionary_hash_postfix
.data(),
416 dictionary_hash_postfix
.size());
417 filter
->FlushStreamBuffer(dictionary_hash_postfix
.size());
419 // With a non-existant dictionary specifier, try to read output.
420 output_bytes_or_buffer_size
= sizeof(output_buffer
);
421 status
= filter
->ReadData(output_buffer
, &output_bytes_or_buffer_size
);
423 EXPECT_EQ(0, output_bytes_or_buffer_size
);
424 EXPECT_EQ(Filter::FILTER_ERROR
, status
);
426 EXPECT_EQ(SDCH_DOMAIN_BLACKLIST_INCLUDES_TARGET
,
427 sdch_manager_
->IsInSupportedDomain(GURL(url_string
)));
428 sdch_manager_
->ClearBlacklistings();
429 EXPECT_EQ(SDCH_OK
, sdch_manager_
->IsInSupportedDomain(GURL(url_string
)));
432 TEST_F(SdchFilterTest
, DictionaryAddOnce
) {
433 // Construct a valid SDCH dictionary from a VCDIFF dictionary.
434 const std::string kSampleDomain
= "sdchtest.com";
435 std::string
dictionary(NewSdchDictionary(kSampleDomain
));
437 std::string url_string
= "http://" + kSampleDomain
;
438 GURL
url(url_string
);
439 EXPECT_TRUE(AddSdchDictionary(dictionary
, url
));
441 // Check we can't add it twice.
442 EXPECT_FALSE(AddSdchDictionary(dictionary
, url
));
444 const std::string kSampleDomain2
= "sdchtest2.com";
446 // Construct a second SDCH dictionary from a VCDIFF dictionary.
447 std::string
dictionary2(NewSdchDictionary(kSampleDomain2
));
449 std::string url_string2
= "http://" + kSampleDomain2
;
450 GURL
url2(url_string2
);
451 EXPECT_TRUE(AddSdchDictionary(dictionary2
, url2
));
454 TEST_F(SdchFilterTest
, BasicDictionary
) {
455 // Construct a valid SDCH dictionary from a VCDIFF dictionary.
456 const std::string kSampleDomain
= "sdchtest.com";
457 std::string
dictionary(NewSdchDictionary(kSampleDomain
));
459 std::string url_string
= "http://" + kSampleDomain
;
461 GURL
url(url_string
);
462 EXPECT_TRUE(AddSdchDictionary(dictionary
, url
));
464 std::string
compressed(NewSdchCompressedData(dictionary
));
466 std::vector
<Filter::FilterType
> filter_types
;
467 filter_types
.push_back(Filter::FILTER_TYPE_SDCH
);
469 SetupFilterContextWithGURL(url
);
471 scoped_ptr
<Filter
> filter(Filter::Factory(filter_types
, *filter_context()));
473 size_t feed_block_size
= 100;
474 size_t output_block_size
= 100;
476 EXPECT_TRUE(FilterTestData(compressed
, feed_block_size
, output_block_size
,
477 filter
.get(), &output
));
478 EXPECT_EQ(output
, expanded_
);
480 // Decode with really small buffers (size 1) to check for edge effects.
481 filter
.reset(Filter::Factory(filter_types
, *filter_context()));
484 output_block_size
= 1;
486 EXPECT_TRUE(FilterTestData(compressed
, feed_block_size
, output_block_size
,
487 filter
.get(), &output
));
488 EXPECT_EQ(output
, expanded_
);
491 TEST_F(SdchFilterTest
, NoDecodeHttps
) {
492 // Construct a valid SDCH dictionary from a VCDIFF dictionary.
493 const std::string kSampleDomain
= "sdchtest.com";
494 std::string
dictionary(NewSdchDictionary(kSampleDomain
));
496 std::string url_string
= "http://" + kSampleDomain
;
498 GURL
url(url_string
);
499 EXPECT_TRUE(AddSdchDictionary(dictionary
, url
));
501 std::string
compressed(NewSdchCompressedData(dictionary
));
503 std::vector
<Filter::FilterType
> filter_types
;
504 filter_types
.push_back(Filter::FILTER_TYPE_SDCH
);
506 GURL
filter_context_gurl("https://" + kSampleDomain
);
507 SetupFilterContextWithGURL(GURL("https://" + kSampleDomain
));
508 scoped_ptr
<Filter
> filter(Filter::Factory(filter_types
, *filter_context()));
510 const size_t feed_block_size(100);
511 const size_t output_block_size(100);
514 EXPECT_FALSE(FilterTestData(compressed
, feed_block_size
, output_block_size
,
515 filter
.get(), &output
));
518 // Current failsafe TODO/hack refuses to decode any content that doesn't use
519 // http as the scheme (see use of DICTIONARY_SELECTED_FOR_NON_HTTP).
520 // The following tests this blockage. Note that blacklisting results, so we
521 // we need separate tests for each of these.
522 TEST_F(SdchFilterTest
, NoDecodeFtp
) {
523 // Construct a valid SDCH dictionary from a VCDIFF dictionary.
524 const std::string kSampleDomain
= "sdchtest.com";
525 std::string
dictionary(NewSdchDictionary(kSampleDomain
));
527 std::string url_string
= "http://" + kSampleDomain
;
529 GURL
url(url_string
);
530 EXPECT_TRUE(AddSdchDictionary(dictionary
, url
));
532 std::string
compressed(NewSdchCompressedData(dictionary
));
534 std::vector
<Filter::FilterType
> filter_types
;
535 filter_types
.push_back(Filter::FILTER_TYPE_SDCH
);
537 SetupFilterContextWithGURL(GURL("ftp://" + kSampleDomain
));
538 scoped_ptr
<Filter
> filter(Filter::Factory(filter_types
, *filter_context()));
540 const size_t feed_block_size(100);
541 const size_t output_block_size(100);
544 EXPECT_FALSE(FilterTestData(compressed
, feed_block_size
, output_block_size
,
545 filter
.get(), &output
));
548 TEST_F(SdchFilterTest
, NoDecodeFileColon
) {
549 // Construct a valid SDCH dictionary from a VCDIFF dictionary.
550 const std::string kSampleDomain
= "sdchtest.com";
551 std::string
dictionary(NewSdchDictionary(kSampleDomain
));
553 std::string url_string
= "http://" + kSampleDomain
;
555 GURL
url(url_string
);
556 EXPECT_TRUE(AddSdchDictionary(dictionary
, url
));
558 std::string
compressed(NewSdchCompressedData(dictionary
));
560 std::vector
<Filter::FilterType
> filter_types
;
561 filter_types
.push_back(Filter::FILTER_TYPE_SDCH
);
563 SetupFilterContextWithGURL(GURL("file://" + kSampleDomain
));
564 scoped_ptr
<Filter
> filter(Filter::Factory(filter_types
, *filter_context()));
566 const size_t feed_block_size(100);
567 const size_t output_block_size(100);
570 EXPECT_FALSE(FilterTestData(compressed
, feed_block_size
, output_block_size
,
571 filter
.get(), &output
));
574 TEST_F(SdchFilterTest
, NoDecodeAboutColon
) {
575 // Construct a valid SDCH dictionary from a VCDIFF dictionary.
576 const std::string kSampleDomain
= "sdchtest.com";
577 std::string
dictionary(NewSdchDictionary(kSampleDomain
));
579 std::string url_string
= "http://" + kSampleDomain
;
581 GURL
url(url_string
);
582 EXPECT_TRUE(AddSdchDictionary(dictionary
, url
));
584 std::string
compressed(NewSdchCompressedData(dictionary
));
586 std::vector
<Filter::FilterType
> filter_types
;
587 filter_types
.push_back(Filter::FILTER_TYPE_SDCH
);
589 SetupFilterContextWithGURL(GURL("about://" + kSampleDomain
));
590 scoped_ptr
<Filter
> filter(Filter::Factory(filter_types
, *filter_context()));
592 const size_t feed_block_size(100);
593 const size_t output_block_size(100);
596 EXPECT_FALSE(FilterTestData(compressed
, feed_block_size
, output_block_size
,
597 filter
.get(), &output
));
600 TEST_F(SdchFilterTest
, NoDecodeJavaScript
) {
601 // Construct a valid SDCH dictionary from a VCDIFF dictionary.
602 const std::string kSampleDomain
= "sdchtest.com";
603 std::string
dictionary(NewSdchDictionary(kSampleDomain
));
605 std::string url_string
= "http://" + kSampleDomain
;
607 GURL
url(url_string
);
608 EXPECT_TRUE(AddSdchDictionary(dictionary
, url
));
610 std::string
compressed(NewSdchCompressedData(dictionary
));
612 std::vector
<Filter::FilterType
> filter_types
;
613 filter_types
.push_back(Filter::FILTER_TYPE_SDCH
);
615 SetupFilterContextWithGURL(GURL("javascript://" + kSampleDomain
));
616 scoped_ptr
<Filter
> filter(Filter::Factory(filter_types
, *filter_context()));
618 const size_t feed_block_size(100);
619 const size_t output_block_size(100);
622 EXPECT_FALSE(FilterTestData(compressed
, feed_block_size
, output_block_size
,
623 filter
.get(), &output
));
626 TEST_F(SdchFilterTest
, CanStillDecodeHttp
) {
627 // Construct a valid SDCH dictionary from a VCDIFF dictionary.
628 const std::string kSampleDomain
= "sdchtest.com";
629 std::string
dictionary(NewSdchDictionary(kSampleDomain
));
631 std::string url_string
= "http://" + kSampleDomain
;
633 GURL
url(url_string
);
634 EXPECT_TRUE(AddSdchDictionary(dictionary
, url
));
636 std::string
compressed(NewSdchCompressedData(dictionary
));
638 std::vector
<Filter::FilterType
> filter_types
;
639 filter_types
.push_back(Filter::FILTER_TYPE_SDCH
);
641 SetupFilterContextWithGURL(GURL("http://" + kSampleDomain
));
642 scoped_ptr
<Filter
> filter(Filter::Factory(filter_types
, *filter_context()));
644 const size_t feed_block_size(100);
645 const size_t output_block_size(100);
648 base::HistogramTester tester
;
650 EXPECT_TRUE(FilterTestData(compressed
, feed_block_size
, output_block_size
,
651 filter
.get(), &output
));
652 // The filter's destructor is responsible for uploading total ratio
656 tester
.ExpectTotalCount("Sdch3.Network_Decode_Ratio_a", 1);
657 tester
.ExpectTotalCount("Sdch3.NetworkBytesSavedByCompression", 1);
660 TEST_F(SdchFilterTest
, CrossDomainDictionaryUse
) {
661 // Construct a valid SDCH dictionary from a VCDIFF dictionary.
662 const std::string kSampleDomain
= "sdchtest.com";
663 std::string
dictionary(NewSdchDictionary(kSampleDomain
));
665 std::string url_string
= "http://" + kSampleDomain
;
667 GURL
url(url_string
);
668 EXPECT_TRUE(AddSdchDictionary(dictionary
, url
));
670 std::string
compressed(NewSdchCompressedData(dictionary
));
672 std::vector
<Filter::FilterType
> filter_types
;
673 filter_types
.push_back(Filter::FILTER_TYPE_SDCH
);
675 // Decode with content arriving from the "wrong" domain.
676 // This tests SdchManager::CanSet().
677 GURL
wrong_domain_url("http://www.wrongdomain.com");
678 SetupFilterContextWithGURL(wrong_domain_url
);
679 scoped_ptr
<Filter
> filter(Filter::Factory(filter_types
, *filter_context()));
681 size_t feed_block_size
= 100;
682 size_t output_block_size
= 100;
684 EXPECT_FALSE(FilterTestData(compressed
, feed_block_size
, output_block_size
,
685 filter
.get(), &output
));
686 EXPECT_EQ(output
.size(), 0u); // No output written.
688 EXPECT_EQ(SDCH_OK
, sdch_manager_
->IsInSupportedDomain(GURL(url_string
)));
689 EXPECT_EQ(SDCH_DOMAIN_BLACKLIST_INCLUDES_TARGET
,
690 sdch_manager_
->IsInSupportedDomain(wrong_domain_url
));
691 sdch_manager_
->ClearBlacklistings();
692 EXPECT_EQ(SDCH_OK
, sdch_manager_
->IsInSupportedDomain(wrong_domain_url
));
695 TEST_F(SdchFilterTest
, DictionaryPathValidation
) {
696 // Construct a valid SDCH dictionary from a VCDIFF dictionary.
697 const std::string kSampleDomain
= "sdchtest.com";
698 std::string
dictionary(NewSdchDictionary(kSampleDomain
));
700 std::string url_string
= "http://" + kSampleDomain
;
702 GURL
url(url_string
);
703 EXPECT_TRUE(AddSdchDictionary(dictionary
, url
));
705 // Create a dictionary with a path restriction, by prefixing dictionary.
706 const std::string
path("/special_path/bin");
707 std::string
dictionary_with_path("Path: " + path
+ "\n");
708 dictionary_with_path
.append(dictionary
);
709 GURL
url2(url_string
+ path
);
710 EXPECT_TRUE(AddSdchDictionary(dictionary_with_path
, url2
));
712 std::string
compressed_for_path(NewSdchCompressedData(dictionary_with_path
));
714 std::vector
<Filter::FilterType
> filter_types
;
715 filter_types
.push_back(Filter::FILTER_TYPE_SDCH
);
717 // Test decode the path data, arriving from a valid path.
718 SetupFilterContextWithGURL(GURL(url_string
+ path
));
719 scoped_ptr
<Filter
> filter(Filter::Factory(filter_types
, *filter_context()));
721 size_t feed_block_size
= 100;
722 size_t output_block_size
= 100;
725 EXPECT_TRUE(FilterTestData(compressed_for_path
, feed_block_size
,
726 output_block_size
, filter
.get(), &output
));
727 EXPECT_EQ(output
, expanded_
);
729 // Test decode the path data, arriving from a invalid path.
730 SetupFilterContextWithGURL(GURL(url_string
));
731 filter
.reset(Filter::Factory(filter_types
, *filter_context()));
733 feed_block_size
= 100;
734 output_block_size
= 100;
736 EXPECT_FALSE(FilterTestData(compressed_for_path
, feed_block_size
,
737 output_block_size
, filter
.get(), &output
));
738 EXPECT_EQ(output
.size(), 0u); // No output written.
740 EXPECT_EQ(SDCH_DOMAIN_BLACKLIST_INCLUDES_TARGET
,
741 sdch_manager_
->IsInSupportedDomain(GURL(url_string
)));
742 sdch_manager_
->ClearBlacklistings();
743 EXPECT_EQ(SDCH_OK
, sdch_manager_
->IsInSupportedDomain(GURL(url_string
)));
746 TEST_F(SdchFilterTest
, DictionaryPortValidation
) {
747 // Construct a valid SDCH dictionary from a VCDIFF dictionary.
748 const std::string kSampleDomain
= "sdchtest.com";
749 std::string
dictionary(NewSdchDictionary(kSampleDomain
));
751 std::string url_string
= "http://" + kSampleDomain
;
753 GURL
url(url_string
);
754 EXPECT_TRUE(AddSdchDictionary(dictionary
, url
));
756 // Create a dictionary with a port restriction, by prefixing old dictionary.
757 const std::string
port("502");
758 std::string
dictionary_with_port("Port: " + port
+ "\n");
759 dictionary_with_port
.append("Port: 80\n"); // Add default port.
760 dictionary_with_port
.append(dictionary
);
761 EXPECT_TRUE(AddSdchDictionary(dictionary_with_port
,
762 GURL(url_string
+ ":" + port
)));
764 std::string
compressed_for_port(NewSdchCompressedData(dictionary_with_port
));
766 std::vector
<Filter::FilterType
> filter_types
;
767 filter_types
.push_back(Filter::FILTER_TYPE_SDCH
);
769 // Test decode the port data, arriving from a valid port.
770 SetupFilterContextWithGURL(GURL(url_string
+ ":" + port
));
771 scoped_ptr
<Filter
> filter(Filter::Factory(filter_types
, *filter_context()));
773 size_t feed_block_size
= 100;
774 size_t output_block_size
= 100;
776 EXPECT_TRUE(FilterTestData(compressed_for_port
, feed_block_size
,
777 output_block_size
, filter
.get(), &output
));
778 EXPECT_EQ(output
, expanded_
);
780 // Test decode the port data, arriving from a valid (default) port.
781 SetupFilterContextWithGURL(GURL(url_string
)); // Default port.
782 filter
.reset(Filter::Factory(filter_types
, *filter_context()));
784 feed_block_size
= 100;
785 output_block_size
= 100;
787 EXPECT_TRUE(FilterTestData(compressed_for_port
, feed_block_size
,
788 output_block_size
, filter
.get(), &output
));
789 EXPECT_EQ(output
, expanded_
);
791 // Test decode the port data, arriving from a invalid port.
792 SetupFilterContextWithGURL(GURL(url_string
+ ":" + port
+ "1"));
793 filter
.reset(Filter::Factory(filter_types
, *filter_context()));
795 feed_block_size
= 100;
796 output_block_size
= 100;
798 EXPECT_FALSE(FilterTestData(compressed_for_port
, feed_block_size
,
799 output_block_size
, filter
.get(), &output
));
800 EXPECT_EQ(output
.size(), 0u); // No output written.
802 EXPECT_EQ(SDCH_DOMAIN_BLACKLIST_INCLUDES_TARGET
,
803 sdch_manager_
->IsInSupportedDomain(GURL(url_string
)));
804 sdch_manager_
->ClearBlacklistings();
805 EXPECT_EQ(SDCH_OK
, sdch_manager_
->IsInSupportedDomain(GURL(url_string
)));
808 // Helper function to perform gzip compression of data.
809 static std::string
gzip_compress(const std::string
&input
) {
810 z_stream zlib_stream
;
811 memset(&zlib_stream
, 0, sizeof(zlib_stream
));
815 code
= deflateInit2(&zlib_stream
, Z_DEFAULT_COMPRESSION
, Z_DEFLATED
,
820 CHECK_EQ(Z_OK
, code
);
822 // Fill in zlib control block
823 zlib_stream
.next_in
= bit_cast
<Bytef
*>(input
.data());
824 zlib_stream
.avail_in
= input
.size();
826 // Assume we can compress into similar buffer (add 100 bytes to be sure).
827 size_t gzip_compressed_length
= zlib_stream
.avail_in
+ 100;
828 scoped_ptr
<char[]> gzip_compressed(new char[gzip_compressed_length
]);
829 zlib_stream
.next_out
= bit_cast
<Bytef
*>(gzip_compressed
.get());
830 zlib_stream
.avail_out
= gzip_compressed_length
;
832 // The GZIP header (see RFC 1952):
833 // +---+---+---+---+---+---+---+---+---+---+
834 // |ID1|ID2|CM |FLG| MTIME |XFL|OS |
835 // +---+---+---+---+---+---+---+---+---+---+
838 // CM \010 (compression method == DEFLATE)
839 // FLG \000 (special flags that we do not support)
840 // MTIME Unix format modification time (0 means not available)
841 // XFL 2-4? DEFLATE flags
842 // OS ???? Operating system indicator (255 means unknown)
844 // Header value we generate:
845 const char kGZipHeader
[] = { '\037', '\213', '\010', '\000', '\000',
846 '\000', '\000', '\000', '\002', '\377' };
847 CHECK_GT(zlib_stream
.avail_out
, sizeof(kGZipHeader
));
848 memcpy(zlib_stream
.next_out
, kGZipHeader
, sizeof(kGZipHeader
));
849 zlib_stream
.next_out
+= sizeof(kGZipHeader
);
850 zlib_stream
.avail_out
-= sizeof(kGZipHeader
);
853 code
= deflate(&zlib_stream
, Z_FINISH
);
854 gzip_compressed_length
-= zlib_stream
.avail_out
;
855 std::string
compressed(gzip_compressed
.get(), gzip_compressed_length
);
856 deflateEnd(&zlib_stream
);
860 //------------------------------------------------------------------------------
862 class SdchFilterChainingTest
{
864 static Filter
* Factory(const std::vector
<Filter::FilterType
>& types
,
865 const FilterContext
& context
, int size
) {
866 return Filter::FactoryForTests(types
, context
, size
);
870 // Test that filters can be cascaded (chained) so that the output of one filter
871 // is processed by the next one. This is most critical for SDCH, which is
872 // routinely followed by gzip (during encoding). The filter we'll test for will
873 // do the gzip decoding first, and then decode the SDCH content.
874 TEST_F(SdchFilterTest
, FilterChaining
) {
875 // Construct a valid SDCH dictionary from a VCDIFF dictionary.
876 const std::string kSampleDomain
= "sdchtest.com";
877 std::string
dictionary(NewSdchDictionary(kSampleDomain
));
879 std::string url_string
= "http://" + kSampleDomain
;
881 GURL
url(url_string
);
882 EXPECT_TRUE(AddSdchDictionary(dictionary
, url
));
884 std::string
sdch_compressed(NewSdchCompressedData(dictionary
));
886 // Use Gzip to compress the sdch sdch_compressed data.
887 std::string gzip_compressed_sdch
= gzip_compress(sdch_compressed
);
889 // Construct a chained filter.
890 std::vector
<Filter::FilterType
> filter_types
;
891 filter_types
.push_back(Filter::FILTER_TYPE_SDCH
);
892 filter_types
.push_back(Filter::FILTER_TYPE_GZIP
);
894 // First try with a large buffer (larger than test input, or compressed data).
895 const size_t kLargeInputBufferSize(1000); // Used internally in filters.
896 CHECK_GT(kLargeInputBufferSize
, gzip_compressed_sdch
.size());
897 CHECK_GT(kLargeInputBufferSize
, sdch_compressed
.size());
898 CHECK_GT(kLargeInputBufferSize
, expanded_
.size());
899 SetupFilterContextWithGURL(url
);
900 scoped_ptr
<Filter
> filter(
901 SdchFilterChainingTest::Factory(filter_types
, *filter_context(),
902 kLargeInputBufferSize
));
903 EXPECT_EQ(static_cast<int>(kLargeInputBufferSize
),
904 filter
->stream_buffer_size());
906 // Verify that chained filter is waiting for data.
907 char tiny_output_buffer
[10];
908 int tiny_output_size
= sizeof(tiny_output_buffer
);
909 EXPECT_EQ(Filter::FILTER_NEED_MORE_DATA
,
910 filter
->ReadData(tiny_output_buffer
, &tiny_output_size
));
912 // Make chain process all data.
913 size_t feed_block_size
= kLargeInputBufferSize
;
914 size_t output_block_size
= kLargeInputBufferSize
;
916 EXPECT_TRUE(FilterTestData(gzip_compressed_sdch
, feed_block_size
,
917 output_block_size
, filter
.get(), &output
));
918 EXPECT_EQ(output
, expanded_
);
920 // Next try with a mid-sized internal buffer size.
921 const size_t kMidSizedInputBufferSize(100);
922 // Buffer should be big enough to swallow whole gzip content.
923 CHECK_GT(kMidSizedInputBufferSize
, gzip_compressed_sdch
.size());
924 // Buffer should be small enough that entire SDCH content can't fit.
925 // We'll go even further, and force the chain to flush the buffer between the
926 // two filters more than once (that is why we multiply by 2).
927 CHECK_LT(kMidSizedInputBufferSize
* 2, sdch_compressed
.size());
928 filter_context()->SetURL(url
);
930 SdchFilterChainingTest::Factory(filter_types
, *filter_context(),
931 kMidSizedInputBufferSize
));
932 EXPECT_EQ(static_cast<int>(kMidSizedInputBufferSize
),
933 filter
->stream_buffer_size());
935 feed_block_size
= kMidSizedInputBufferSize
;
936 output_block_size
= kMidSizedInputBufferSize
;
938 EXPECT_TRUE(FilterTestData(gzip_compressed_sdch
, feed_block_size
,
939 output_block_size
, filter
.get(), &output
));
940 EXPECT_EQ(output
, expanded_
);
942 // Next try with a tiny input and output buffer to cover edge effects.
943 filter
.reset(SdchFilterChainingTest::Factory(filter_types
, *filter_context(),
944 kLargeInputBufferSize
));
945 EXPECT_EQ(static_cast<int>(kLargeInputBufferSize
),
946 filter
->stream_buffer_size());
949 output_block_size
= 1;
951 EXPECT_TRUE(FilterTestData(gzip_compressed_sdch
, feed_block_size
,
952 output_block_size
, filter
.get(), &output
));
953 EXPECT_EQ(output
, expanded_
);
956 TEST_F(SdchFilterTest
, DefaultGzipIfSdch
) {
957 // Construct a valid SDCH dictionary from a VCDIFF dictionary.
958 const std::string kSampleDomain
= "sdchtest.com";
959 std::string
dictionary(NewSdchDictionary(kSampleDomain
));
961 std::string url_string
= "http://" + kSampleDomain
;
963 GURL
url(url_string
);
964 EXPECT_TRUE(AddSdchDictionary(dictionary
, url
));
966 std::string
sdch_compressed(NewSdchCompressedData(dictionary
));
968 // Use Gzip to compress the sdch sdch_compressed data.
969 std::string gzip_compressed_sdch
= gzip_compress(sdch_compressed
);
971 // Only claim to have sdch content, but really use the gzipped sdch content.
972 // System should automatically add the missing (optional) gzip.
973 std::vector
<Filter::FilterType
> filter_types
;
974 filter_types
.push_back(Filter::FILTER_TYPE_SDCH
);
976 filter_context()->SetMimeType("anything/mime");
977 SetupFilterContextWithGURL(url
);
979 Filter::FixupEncodingTypes(*filter_context(), &filter_types
);
980 ASSERT_EQ(filter_types
.size(), 2u);
981 EXPECT_EQ(filter_types
[0], Filter::FILTER_TYPE_SDCH
);
982 EXPECT_EQ(filter_types
[1], Filter::FILTER_TYPE_GZIP_HELPING_SDCH
);
984 // First try with a large buffer (larger than test input, or compressed data).
985 scoped_ptr
<Filter
> filter(Filter::Factory(filter_types
, *filter_context()));
987 // Verify that chained filter is waiting for data.
988 char tiny_output_buffer
[10];
989 int tiny_output_size
= sizeof(tiny_output_buffer
);
990 EXPECT_EQ(Filter::FILTER_NEED_MORE_DATA
,
991 filter
->ReadData(tiny_output_buffer
, &tiny_output_size
));
993 size_t feed_block_size
= 100;
994 size_t output_block_size
= 100;
996 EXPECT_TRUE(FilterTestData(gzip_compressed_sdch
, feed_block_size
,
997 output_block_size
, filter
.get(), &output
));
998 EXPECT_EQ(output
, expanded_
);
1000 // Next try with a tiny buffer to cover edge effects.
1001 filter
.reset(Filter::Factory(filter_types
, *filter_context()));
1003 feed_block_size
= 1;
1004 output_block_size
= 1;
1006 EXPECT_TRUE(FilterTestData(gzip_compressed_sdch
, feed_block_size
,
1007 output_block_size
, filter
.get(), &output
));
1008 EXPECT_EQ(output
, expanded_
);
1011 TEST_F(SdchFilterTest
, AcceptGzipSdchIfGzip
) {
1012 // Construct a valid SDCH dictionary from a VCDIFF dictionary.
1013 const std::string kSampleDomain
= "sdchtest.com";
1014 std::string
dictionary(NewSdchDictionary(kSampleDomain
));
1016 std::string url_string
= "http://" + kSampleDomain
;
1018 GURL
url(url_string
);
1019 EXPECT_TRUE(AddSdchDictionary(dictionary
, url
));
1021 std::string
sdch_compressed(NewSdchCompressedData(dictionary
));
1023 // Use Gzip to compress the sdch sdch_compressed data.
1024 std::string gzip_compressed_sdch
= gzip_compress(sdch_compressed
);
1026 // Some proxies strip the content encoding statement down to a mere gzip, but
1027 // pass through the original content (with full sdch,gzip encoding).
1028 // Only claim to have gzip content, but really use the gzipped sdch content.
1029 // System should automatically add the missing (optional) sdch.
1030 std::vector
<Filter::FilterType
> filter_types
;
1031 filter_types
.push_back(Filter::FILTER_TYPE_GZIP
);
1033 filter_context()->SetMimeType("anything/mime");
1034 SetupFilterContextWithGURL(url
);
1035 Filter::FixupEncodingTypes(*filter_context(), &filter_types
);
1036 ASSERT_EQ(filter_types
.size(), 3u);
1037 EXPECT_EQ(filter_types
[0], Filter::FILTER_TYPE_SDCH_POSSIBLE
);
1038 EXPECT_EQ(filter_types
[1], Filter::FILTER_TYPE_GZIP_HELPING_SDCH
);
1039 EXPECT_EQ(filter_types
[2], Filter::FILTER_TYPE_GZIP
);
1041 // First try with a large buffer (larger than test input, or compressed data).
1042 scoped_ptr
<Filter
> filter(Filter::Factory(filter_types
, *filter_context()));
1044 // Verify that chained filter is waiting for data.
1045 char tiny_output_buffer
[10];
1046 int tiny_output_size
= sizeof(tiny_output_buffer
);
1047 EXPECT_EQ(Filter::FILTER_NEED_MORE_DATA
,
1048 filter
->ReadData(tiny_output_buffer
, &tiny_output_size
));
1050 size_t feed_block_size
= 100;
1051 size_t output_block_size
= 100;
1053 EXPECT_TRUE(FilterTestData(gzip_compressed_sdch
, feed_block_size
,
1054 output_block_size
, filter
.get(), &output
));
1055 EXPECT_EQ(output
, expanded_
);
1057 // Next try with a tiny buffer to cover edge effects.
1058 filter
.reset(Filter::Factory(filter_types
, *filter_context()));
1060 feed_block_size
= 1;
1061 output_block_size
= 1;
1063 EXPECT_TRUE(FilterTestData(gzip_compressed_sdch
, feed_block_size
,
1064 output_block_size
, filter
.get(), &output
));
1065 EXPECT_EQ(output
, expanded_
);
1068 TEST_F(SdchFilterTest
, DefaultSdchGzipIfEmpty
) {
1069 // Construct a valid SDCH dictionary from a VCDIFF dictionary.
1070 const std::string kSampleDomain
= "sdchtest.com";
1071 std::string
dictionary(NewSdchDictionary(kSampleDomain
));
1073 std::string url_string
= "http://" + kSampleDomain
;
1075 GURL
url(url_string
);
1076 EXPECT_TRUE(AddSdchDictionary(dictionary
, url
));
1078 std::string
sdch_compressed(NewSdchCompressedData(dictionary
));
1080 // Use Gzip to compress the sdch sdch_compressed data.
1081 std::string gzip_compressed_sdch
= gzip_compress(sdch_compressed
);
1083 // Only claim to have non-encoded content, but really use the gzipped sdch
1085 // System should automatically add the missing (optional) sdch,gzip.
1086 std::vector
<Filter::FilterType
> filter_types
;
1088 filter_context()->SetMimeType("anything/mime");
1089 SetupFilterContextWithGURL(url
);
1090 Filter::FixupEncodingTypes(*filter_context(), &filter_types
);
1091 ASSERT_EQ(filter_types
.size(), 2u);
1092 EXPECT_EQ(filter_types
[0], Filter::FILTER_TYPE_SDCH_POSSIBLE
);
1093 EXPECT_EQ(filter_types
[1], Filter::FILTER_TYPE_GZIP_HELPING_SDCH
);
1095 // First try with a large buffer (larger than test input, or compressed data).
1096 scoped_ptr
<Filter
> filter(Filter::Factory(filter_types
, *filter_context()));
1098 // Verify that chained filter is waiting for data.
1099 char tiny_output_buffer
[10];
1100 int tiny_output_size
= sizeof(tiny_output_buffer
);
1101 EXPECT_EQ(Filter::FILTER_NEED_MORE_DATA
,
1102 filter
->ReadData(tiny_output_buffer
, &tiny_output_size
));
1104 size_t feed_block_size
= 100;
1105 size_t output_block_size
= 100;
1107 EXPECT_TRUE(FilterTestData(gzip_compressed_sdch
, feed_block_size
,
1108 output_block_size
, filter
.get(), &output
));
1109 EXPECT_EQ(output
, expanded_
);
1111 // Next try with a tiny buffer to cover edge effects.
1112 filter
.reset(Filter::Factory(filter_types
, *filter_context()));
1114 feed_block_size
= 1;
1115 output_block_size
= 1;
1117 EXPECT_TRUE(FilterTestData(gzip_compressed_sdch
, feed_block_size
,
1118 output_block_size
, filter
.get(), &output
));
1119 EXPECT_EQ(output
, expanded_
);
1122 TEST_F(SdchFilterTest
, AcceptGzipGzipSdchIfGzip
) {
1123 // Construct a valid SDCH dictionary from a VCDIFF dictionary.
1124 const std::string kSampleDomain
= "sdchtest.com";
1125 std::string
dictionary(NewSdchDictionary(kSampleDomain
));
1127 std::string url_string
= "http://" + kSampleDomain
;
1129 GURL
url(url_string
);
1130 EXPECT_TRUE(AddSdchDictionary(dictionary
, url
));
1132 std::string
sdch_compressed(NewSdchCompressedData(dictionary
));
1134 // Vodaphone (UK) Mobile Broadband provides double gzipped sdch with a content
1135 // encoding of merely gzip (apparently, only listing the extra level of
1136 // wrapper compression they added, but discarding the actual content encoding.
1137 // Use Gzip to double compress the sdch sdch_compressed data.
1138 std::string double_gzip_compressed_sdch
= gzip_compress(gzip_compress(
1141 // Only claim to have gzip content, but really use the double gzipped sdch
1143 // System should automatically add the missing (optional) sdch, gzip decoders.
1144 std::vector
<Filter::FilterType
> filter_types
;
1145 filter_types
.push_back(Filter::FILTER_TYPE_GZIP
);
1147 filter_context()->SetMimeType("anything/mime");
1148 SetupFilterContextWithGURL(url
);
1149 Filter::FixupEncodingTypes(*filter_context(), &filter_types
);
1150 ASSERT_EQ(filter_types
.size(), 3u);
1151 EXPECT_EQ(filter_types
[0], Filter::FILTER_TYPE_SDCH_POSSIBLE
);
1152 EXPECT_EQ(filter_types
[1], Filter::FILTER_TYPE_GZIP_HELPING_SDCH
);
1153 EXPECT_EQ(filter_types
[2], Filter::FILTER_TYPE_GZIP
);
1155 // First try with a large buffer (larger than test input, or compressed data).
1156 scoped_ptr
<Filter
> filter(Filter::Factory(filter_types
, *filter_context()));
1158 // Verify that chained filter is waiting for data.
1159 char tiny_output_buffer
[10];
1160 int tiny_output_size
= sizeof(tiny_output_buffer
);
1161 EXPECT_EQ(Filter::FILTER_NEED_MORE_DATA
,
1162 filter
->ReadData(tiny_output_buffer
, &tiny_output_size
));
1164 size_t feed_block_size
= 100;
1165 size_t output_block_size
= 100;
1167 EXPECT_TRUE(FilterTestData(double_gzip_compressed_sdch
, feed_block_size
,
1168 output_block_size
, filter
.get(), &output
));
1169 EXPECT_EQ(output
, expanded_
);
1171 // Next try with a tiny buffer to cover edge effects.
1172 filter
.reset(Filter::Factory(filter_types
, *filter_context()));
1174 feed_block_size
= 1;
1175 output_block_size
= 1;
1177 EXPECT_TRUE(FilterTestData(double_gzip_compressed_sdch
, feed_block_size
,
1178 output_block_size
, filter
.get(), &output
));
1179 EXPECT_EQ(output
, expanded_
);
1182 // Test to make sure we decode properly with an unexpected dictionary.
1183 TEST_F(SdchFilterTest
, UnexpectedDictionary
) {
1184 // Setup a dictionary, add it to the filter context, and create a filter
1185 // based on that dictionary.
1186 const std::string kSampleDomain
= "sdchtest.com";
1187 std::string
dictionary(NewSdchDictionary(kSampleDomain
));
1188 std::string url_string
= "http://" + kSampleDomain
;
1189 GURL
url(url_string
);
1190 EXPECT_TRUE(AddSdchDictionary(dictionary
, url
));
1192 SetupFilterContextWithGURL(url
);
1194 std::vector
<Filter::FilterType
> filter_types
;
1195 filter_types
.push_back(Filter::FILTER_TYPE_SDCH
);
1196 scoped_ptr
<Filter
> filter(Filter::Factory(filter_types
, *filter_context()));
1198 // Setup another dictionary, expired. Don't add it to the filter context.
1199 // Delete stored dictionaries first to handle platforms which only
1200 // have room for a single dictionary.
1201 sdch_manager_
->ClearData();
1202 std::string
expired_dictionary(NewSdchExpiredDictionary(kSampleDomain
));
1204 // Don't use the Helper function since its insertion check is indeterminate
1205 // for a Max-Age: 0 dictionary.
1206 sdch_manager_
->AddSdchDictionary(expired_dictionary
, url
, nullptr);
1208 std::string client_hash
;
1209 std::string server_hash
;
1210 SdchManager::GenerateHash(expired_dictionary
, &client_hash
, &server_hash
);
1212 // Make sure Max-Age: 0 shows up as expired.
1213 scoped_ptr
<base::SimpleTestClock
> clock(new base::SimpleTestClock
);
1214 clock
->SetNow(base::Time::Now());
1215 clock
->Advance(base::TimeDelta::FromMinutes(5));
1216 SdchProblemCode problem_code
;
1217 scoped_ptr
<SdchManager::DictionarySet
> hash_set(
1218 sdch_manager_
->GetDictionarySetByHash(
1219 url
, server_hash
, &problem_code
).Pass());
1220 ASSERT_TRUE(hash_set
);
1221 ASSERT_EQ(SDCH_OK
, problem_code
);
1223 const_cast<SdchManager::Dictionary
*>(
1224 hash_set
->GetDictionary(server_hash
))->SetClockForTesting(
1227 // Encode output with the second dictionary.
1228 std::string
sdch_compressed(NewSdchCompressedData(expired_dictionary
));
1230 // See if the filter decodes it.
1232 EXPECT_TRUE(FilterTestData(sdch_compressed
, 100, 100, filter
.get(), &output
));
1233 EXPECT_EQ(expanded_
, output
);
1236 class SimpleSdchObserver
: public SdchObserver
{
1238 explicit SimpleSdchObserver(SdchManager
* manager
)
1239 : dictionary_used_(0), manager_(manager
) {
1240 manager_
->AddObserver(this);
1242 ~SimpleSdchObserver() override
{ manager_
->RemoveObserver(this); }
1245 void OnDictionaryUsed(SdchManager
* manager
,
1246 const std::string
& server_hash
) override
{
1248 last_server_hash_
= server_hash
;
1251 int dictionary_used_calls() const { return dictionary_used_
; }
1252 std::string
last_server_hash() const { return last_server_hash_
; }
1254 void OnGetDictionary(SdchManager
* /* manager */,
1255 const GURL
& /* request_url */,
1256 const GURL
& /* dictionary_url */) override
{}
1257 void OnClearDictionaries(SdchManager
* /* manager */) override
{}
1260 int dictionary_used_
;
1261 std::string last_server_hash_
;
1262 SdchManager
* manager_
;
1264 DISALLOW_COPY_AND_ASSIGN(SimpleSdchObserver
);
1267 TEST_F(SdchFilterTest
, DictionaryUsedSignaled
) {
1268 // Construct a valid SDCH dictionary from a VCDIFF dictionary.
1269 const std::string kSampleDomain
= "sdchtest.com";
1270 std::string
dictionary(NewSdchDictionary(kSampleDomain
));
1271 SimpleSdchObserver
observer(sdch_manager_
.get());
1273 std::string url_string
= "http://" + kSampleDomain
;
1275 GURL
url(url_string
);
1276 EXPECT_TRUE(AddSdchDictionary(dictionary
, url
));
1278 std::string client_hash
;
1279 std::string server_hash
;
1280 SdchManager::GenerateHash(dictionary
, &client_hash
, &server_hash
);
1282 std::string
compressed(NewSdchCompressedData(dictionary
));
1284 std::vector
<Filter::FilterType
> filter_types
;
1285 filter_types
.push_back(Filter::FILTER_TYPE_SDCH
);
1287 SetupFilterContextWithGURL(url
);
1289 scoped_ptr
<Filter
> filter(Filter::Factory(filter_types
, *filter_context()));
1291 size_t feed_block_size
= 100;
1292 size_t output_block_size
= 100;
1294 EXPECT_TRUE(FilterTestData(compressed
, feed_block_size
, output_block_size
,
1295 filter
.get(), &output
));
1296 EXPECT_EQ(output
, expanded_
);
1298 filter
.reset(nullptr);
1300 // Confirm that we got a "DictionaryUsed" signal from the SdchManager
1301 // for our dictionary.
1302 EXPECT_EQ(1, observer
.dictionary_used_calls());
1303 EXPECT_EQ(server_hash
, observer
.last_server_hash());