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/base/sdch_observer.h"
16 #include "net/filter/mock_filter_context.h"
17 #include "net/filter/sdch_filter.h"
18 #include "net/url_request/url_request_context.h"
19 #include "net/url_request/url_request_http_job.h"
20 #include "testing/gtest/include/gtest/gtest.h"
21 #include "third_party/zlib/zlib.h"
25 //------------------------------------------------------------------------------
26 // Provide sample data and compression results with a sample VCDIFF dictionary.
27 // Note an SDCH dictionary has extra meta-data before the VCDIFF dictionary.
28 static const char kTestVcdiffDictionary
[] = "DictionaryFor"
29 "SdchCompression1SdchCompression2SdchCompression3SdchCompression\n";
30 // Pre-compression test data. Note that we pad with a lot of highly gzip
31 // compressible content to help to exercise the chaining pipeline. That is why
32 // there are a PILE of zeros at the start and end.
33 // This will ensure that gzip compressed data can be fed to the chain in one
34 // gulp, but (with careful selection of intermediate buffers) that it takes
35 // several sdch buffers worth of data to satisfy the sdch filter. See detailed
36 // CHECK() calls in FilterChaining test for specifics.
37 static const char kTestData
[] = "0000000000000000000000000000000000000000000000"
38 "0000000000000000000000000000TestData "
39 "SdchCompression1SdchCompression2SdchCompression3SdchCompression"
40 "00000000000000000000000000000000000000000000000000000000000000000000000000"
41 "000000000000000000000000000000000000000\n";
43 // Note SDCH compressed data will include a reference to the SDCH dictionary.
44 static const char kSdchCompressedTestData
[] =
45 "\326\303\304\0\0\001M\0\201S\202\004\0\201E\006\001"
46 "00000000000000000000000000000000000000000000000000000000000000000000000000"
47 "TestData 00000000000000000000000000000000000000000000000000000000000000000"
48 "000000000000000000000000000000000000000000000000\n\001S\023\077\001r\r";
50 //------------------------------------------------------------------------------
52 class SdchFilterTest
: public testing::Test
{
55 : test_vcdiff_dictionary_(kTestVcdiffDictionary
,
56 sizeof(kTestVcdiffDictionary
) - 1),
57 vcdiff_compressed_data_(kSdchCompressedTestData
,
58 sizeof(kSdchCompressedTestData
) - 1),
59 expanded_(kTestData
, sizeof(kTestData
) - 1),
60 sdch_manager_(new SdchManager
),
61 filter_context_(new MockFilterContext
) {
62 URLRequestContext
* url_request_context
=
63 filter_context_
->GetModifiableURLRequestContext();
65 url_request_context
->set_sdch_manager(sdch_manager_
.get());
68 // Attempt to add a dictionary to the manager and probe for success or
70 bool AddSdchDictionary(const std::string
& dictionary_text
,
72 return sdch_manager_
->AddSdchDictionary(dictionary_text
, gurl
, nullptr) ==
76 MockFilterContext
* filter_context() { return filter_context_
.get(); }
78 // Sets both the GURL and the SDCH response for a filter context.
79 void SetupFilterContextWithGURL(GURL url
) {
80 filter_context_
->SetURL(url
);
81 filter_context_
->SetSdchResponse(
82 sdch_manager_
->GetDictionarySet(url
).Pass());
85 std::string
NewSdchCompressedData(const std::string dictionary
) {
86 std::string client_hash
;
87 std::string server_hash
;
88 SdchManager::GenerateHash(dictionary
, &client_hash
, &server_hash
);
90 // Build compressed data that refers to our dictionary.
91 std::string
compressed(server_hash
);
92 compressed
.append("\0", 1);
93 compressed
.append(vcdiff_compressed_data_
);
97 const std::string test_vcdiff_dictionary_
;
98 const std::string vcdiff_compressed_data_
;
99 const std::string expanded_
; // Desired final, decompressed data.
101 scoped_ptr
<SdchManager
> sdch_manager_
;
102 scoped_ptr
<MockFilterContext
> filter_context_
;
105 TEST_F(SdchFilterTest
, Hashing
) {
106 std::string client_hash
, server_hash
;
107 std::string
dictionary("test contents");
108 SdchManager::GenerateHash(dictionary
, &client_hash
, &server_hash
);
110 EXPECT_EQ(client_hash
, "lMQBjS3P");
111 EXPECT_EQ(server_hash
, "MyciMVll");
114 //------------------------------------------------------------------------------
115 // Provide a generic helper function for trying to filter data.
116 // This function repeatedly calls the filter to process data, until the entire
117 // source is consumed. The return value from the filter is appended to output.
118 // This allows us to vary input and output block sizes in order to test for edge
119 // effects (boundary effects?) during the filtering process.
120 // This function provides data to the filter in blocks of no-more-than the
121 // specified input_block_length. It allows the filter to fill no more than
122 // output_buffer_length in any one call to proccess (a.k.a., Read) data, and
123 // concatenates all these little output blocks into the singular output string.
124 static bool FilterTestData(const std::string
& source
,
125 size_t input_block_length
,
126 const size_t output_buffer_length
,
127 Filter
* filter
, std::string
* output
) {
128 CHECK_GT(input_block_length
, 0u);
129 Filter::FilterStatus
status(Filter::FILTER_NEED_MORE_DATA
);
130 size_t source_index
= 0;
131 scoped_ptr
<char[]> output_buffer(new char[output_buffer_length
]);
132 size_t input_amount
= std::min(input_block_length
,
133 static_cast<size_t>(filter
->stream_buffer_size()));
136 int copy_amount
= std::min(input_amount
, source
.size() - source_index
);
137 if (copy_amount
> 0 && status
== Filter::FILTER_NEED_MORE_DATA
) {
138 memcpy(filter
->stream_buffer()->data(), source
.data() + source_index
,
140 filter
->FlushStreamBuffer(copy_amount
);
141 source_index
+= copy_amount
;
143 int buffer_length
= output_buffer_length
;
144 status
= filter
->ReadData(output_buffer
.get(), &buffer_length
);
145 output
->append(output_buffer
.get(), buffer_length
);
146 if (status
== Filter::FILTER_ERROR
)
148 // Callers assume that FILTER_OK with no output buffer means FILTER_DONE.
149 if (Filter::FILTER_OK
== status
&& 0 == buffer_length
)
151 if (copy_amount
== 0 && buffer_length
== 0)
156 static std::string
NewSdchDictionary(const std::string
& domain
) {
157 std::string dictionary
;
158 if (!domain
.empty()) {
159 dictionary
.append("Domain: ");
160 dictionary
.append(domain
);
161 dictionary
.append("\n");
163 dictionary
.append("\n");
164 dictionary
.append(kTestVcdiffDictionary
, sizeof(kTestVcdiffDictionary
) - 1);
168 static std::string
NewSdchExpiredDictionary(const std::string
& domain
) {
169 std::string dictionary
;
170 if (!domain
.empty()) {
171 dictionary
.append("Domain: ");
172 dictionary
.append(domain
);
173 dictionary
.append("\n");
175 dictionary
.append("Max-Age: 0\n");
176 dictionary
.append("\n");
177 dictionary
.append(kTestVcdiffDictionary
, sizeof(kTestVcdiffDictionary
) - 1);
181 TEST_F(SdchFilterTest
, EmptyInputOk
) {
182 std::vector
<Filter::FilterType
> filter_types
;
183 filter_types
.push_back(Filter::FILTER_TYPE_SDCH
);
184 char output_buffer
[20];
185 std::string
url_string("http://ignore.com");
186 filter_context()->SetURL(GURL(url_string
));
187 scoped_ptr
<Filter
> filter(Filter::Factory(filter_types
, *filter_context()));
189 // With no input data, try to read output.
190 int output_bytes_or_buffer_size
= sizeof(output_buffer
);
191 Filter::FilterStatus status
= filter
->ReadData(output_buffer
,
192 &output_bytes_or_buffer_size
);
194 EXPECT_EQ(0, output_bytes_or_buffer_size
);
195 EXPECT_EQ(Filter::FILTER_NEED_MORE_DATA
, status
);
198 // Make sure that the filter context has everything that might be
199 // nuked from it during URLRequest teardown before the SdchFilter
201 TEST_F(SdchFilterTest
, SparseContextOk
) {
202 std::vector
<Filter::FilterType
> filter_types
;
203 filter_types
.push_back(Filter::FILTER_TYPE_SDCH
);
204 char output_buffer
[20];
205 std::string
url_string("http://ignore.com");
206 filter_context()->SetURL(GURL(url_string
));
207 scoped_ptr
<Filter
> filter(Filter::Factory(filter_types
, *filter_context()));
209 // With no input data, try to read output.
210 int output_bytes_or_buffer_size
= sizeof(output_buffer
);
211 Filter::FilterStatus status
= filter
->ReadData(output_buffer
,
212 &output_bytes_or_buffer_size
);
214 EXPECT_EQ(0, output_bytes_or_buffer_size
);
215 EXPECT_EQ(Filter::FILTER_NEED_MORE_DATA
, status
);
217 // Partially tear down context. Anything that goes through request()
218 // without checking it for null in the URLRequestJob::HttpFilterContext
219 // implementation is suspect. Everything that does check it for null should
220 // return null. This is to test for incorrectly relying on filter_context()
221 // from the SdchFilter destructor.
222 filter_context()->NukeUnstableInterfaces();
225 TEST_F(SdchFilterTest
, PassThroughWhenTentative
) {
226 std::vector
<Filter::FilterType
> filter_types
;
227 // Selective a tentative filter (which can fall back to pass through).
228 filter_types
.push_back(Filter::FILTER_TYPE_GZIP_HELPING_SDCH
);
229 char output_buffer
[20];
230 // Response code needs to be 200 to allow a pass through.
231 filter_context()->SetResponseCode(200);
232 std::string
url_string("http://ignore.com");
233 filter_context()->SetURL(GURL(url_string
));
234 scoped_ptr
<Filter
> filter(Filter::Factory(filter_types
, *filter_context()));
236 // Supply enough data to force a pass-through mode..
237 std::string
non_gzip_content("not GZIPed data");
239 char* input_buffer
= filter
->stream_buffer()->data();
240 int input_buffer_size
= filter
->stream_buffer_size();
242 EXPECT_LT(static_cast<int>(non_gzip_content
.size()),
244 memcpy(input_buffer
, non_gzip_content
.data(),
245 non_gzip_content
.size());
246 filter
->FlushStreamBuffer(non_gzip_content
.size());
248 // Try to read output.
249 int output_bytes_or_buffer_size
= sizeof(output_buffer
);
250 Filter::FilterStatus status
= filter
->ReadData(output_buffer
,
251 &output_bytes_or_buffer_size
);
253 EXPECT_EQ(non_gzip_content
.size(),
254 static_cast<size_t>(output_bytes_or_buffer_size
));
255 ASSERT_GT(sizeof(output_buffer
),
256 static_cast<size_t>(output_bytes_or_buffer_size
));
257 output_buffer
[output_bytes_or_buffer_size
] = '\0';
258 EXPECT_TRUE(non_gzip_content
== output_buffer
);
259 EXPECT_EQ(Filter::FILTER_NEED_MORE_DATA
, status
);
262 TEST_F(SdchFilterTest
, RefreshBadReturnCode
) {
263 std::vector
<Filter::FilterType
> filter_types
;
264 // Selective a tentative filter (which can fall back to pass through).
265 filter_types
.push_back(Filter::FILTER_TYPE_SDCH_POSSIBLE
);
266 char output_buffer
[20];
267 // Response code needs to be 200 to allow a pass through.
268 filter_context()->SetResponseCode(403);
269 // Meta refresh will only appear for html content
270 filter_context()->SetMimeType("text/html");
271 std::string
url_string("http://ignore.com");
272 filter_context()->SetURL(GURL(url_string
));
273 scoped_ptr
<Filter
> filter(Filter::Factory(filter_types
, *filter_context()));
275 // Supply enough data to force a pass-through mode, which means we have
276 // provided more than 9 characters that can't be a dictionary hash.
277 std::string
non_sdch_content("This is not SDCH");
279 char* input_buffer
= filter
->stream_buffer()->data();
280 int input_buffer_size
= filter
->stream_buffer_size();
282 EXPECT_LT(static_cast<int>(non_sdch_content
.size()),
284 memcpy(input_buffer
, non_sdch_content
.data(),
285 non_sdch_content
.size());
286 filter
->FlushStreamBuffer(non_sdch_content
.size());
288 // Try to read output.
289 int output_bytes_or_buffer_size
= sizeof(output_buffer
);
290 Filter::FilterStatus status
= filter
->ReadData(output_buffer
,
291 &output_bytes_or_buffer_size
);
293 // We should have read a long and complicated meta-refresh request.
294 EXPECT_TRUE(sizeof(output_buffer
) == output_bytes_or_buffer_size
);
295 // Check at least the prefix of the return.
296 EXPECT_EQ(0, strncmp(output_buffer
,
297 "<head><META HTTP-EQUIV=\"Refresh\" CONTENT=\"0\"></head>",
298 sizeof(output_buffer
)));
299 EXPECT_EQ(Filter::FILTER_OK
, status
);
302 TEST_F(SdchFilterTest
, ErrorOnBadReturnCode
) {
303 std::vector
<Filter::FilterType
> filter_types
;
304 // Selective a tentative filter (which can fall back to pass through).
305 filter_types
.push_back(Filter::FILTER_TYPE_SDCH_POSSIBLE
);
306 char output_buffer
[20];
307 // Response code needs to be 200 to allow a pass through.
308 filter_context()->SetResponseCode(403);
309 // Meta refresh will only appear for html content, so set to something else
310 // to induce an error (we can't meta refresh).
311 filter_context()->SetMimeType("anything");
312 std::string
url_string("http://ignore.com");
313 filter_context()->SetURL(GURL(url_string
));
314 scoped_ptr
<Filter
> filter(Filter::Factory(filter_types
, *filter_context()));
316 // Supply enough data to force a pass-through mode, which means we have
317 // provided more than 9 characters that can't be a dictionary hash.
318 std::string
non_sdch_content("This is not SDCH");
320 char* input_buffer
= filter
->stream_buffer()->data();
321 int input_buffer_size
= filter
->stream_buffer_size();
323 EXPECT_LT(static_cast<int>(non_sdch_content
.size()),
325 memcpy(input_buffer
, non_sdch_content
.data(),
326 non_sdch_content
.size());
327 filter
->FlushStreamBuffer(non_sdch_content
.size());
329 // Try to read output.
330 int output_bytes_or_buffer_size
= sizeof(output_buffer
);
331 Filter::FilterStatus status
= filter
->ReadData(output_buffer
,
332 &output_bytes_or_buffer_size
);
334 EXPECT_EQ(0, output_bytes_or_buffer_size
);
335 EXPECT_EQ(Filter::FILTER_ERROR
, status
);
338 TEST_F(SdchFilterTest
, ErrorOnBadReturnCodeWithHtml
) {
339 std::vector
<Filter::FilterType
> filter_types
;
340 // Selective a tentative filter (which can fall back to pass through).
341 filter_types
.push_back(Filter::FILTER_TYPE_SDCH_POSSIBLE
);
342 char output_buffer
[20];
343 // Response code needs to be 200 to allow a pass through.
344 filter_context()->SetResponseCode(403);
345 // Meta refresh will only appear for html content
346 filter_context()->SetMimeType("text/html");
347 std::string
url_string("http://ignore.com");
348 filter_context()->SetURL(GURL(url_string
));
349 scoped_ptr
<Filter
> filter(Filter::Factory(filter_types
, *filter_context()));
351 // Supply enough data to force a pass-through mode, which means we have
352 // provided more than 9 characters that can't be a dictionary hash.
353 std::string
non_sdch_content("This is not SDCH");
355 char* input_buffer
= filter
->stream_buffer()->data();
356 int input_buffer_size
= filter
->stream_buffer_size();
358 EXPECT_LT(static_cast<int>(non_sdch_content
.size()),
360 memcpy(input_buffer
, non_sdch_content
.data(),
361 non_sdch_content
.size());
362 filter
->FlushStreamBuffer(non_sdch_content
.size());
364 // Try to read output.
365 int output_bytes_or_buffer_size
= sizeof(output_buffer
);
366 Filter::FilterStatus status
= filter
->ReadData(output_buffer
,
367 &output_bytes_or_buffer_size
);
369 // We should have read a long and complicated meta-refresh request.
370 EXPECT_EQ(sizeof(output_buffer
),
371 static_cast<size_t>(output_bytes_or_buffer_size
));
372 // Check at least the prefix of the return.
373 EXPECT_EQ(0, strncmp(output_buffer
,
374 "<head><META HTTP-EQUIV=\"Refresh\" CONTENT=\"0\"></head>",
375 sizeof(output_buffer
)));
376 EXPECT_EQ(Filter::FILTER_OK
, status
);
379 TEST_F(SdchFilterTest
, BasicBadDictionary
) {
380 std::vector
<Filter::FilterType
> filter_types
;
381 filter_types
.push_back(Filter::FILTER_TYPE_SDCH
);
382 char output_buffer
[20];
383 std::string
url_string("http://ignore.com");
384 filter_context()->SetURL(GURL(url_string
));
385 scoped_ptr
<Filter
> filter(Filter::Factory(filter_types
, *filter_context()));
387 // Supply bogus data (which doesn't yet specify a full dictionary hash).
388 // Dictionary hash is 8 characters followed by a null.
389 std::string
dictionary_hash_prefix("123");
391 char* input_buffer
= filter
->stream_buffer()->data();
392 int input_buffer_size
= filter
->stream_buffer_size();
394 EXPECT_LT(static_cast<int>(dictionary_hash_prefix
.size()),
396 memcpy(input_buffer
, dictionary_hash_prefix
.data(),
397 dictionary_hash_prefix
.size());
398 filter
->FlushStreamBuffer(dictionary_hash_prefix
.size());
400 // With less than a dictionary specifier, try to read output.
401 int output_bytes_or_buffer_size
= sizeof(output_buffer
);
402 Filter::FilterStatus status
= filter
->ReadData(output_buffer
,
403 &output_bytes_or_buffer_size
);
405 EXPECT_EQ(0, output_bytes_or_buffer_size
);
406 EXPECT_EQ(Filter::FILTER_NEED_MORE_DATA
, status
);
408 // Provide enough data to complete *a* hash, but it is bogus, and not in our
409 // list of dictionaries, so the filter should error out immediately.
410 std::string
dictionary_hash_postfix("4abcd\0", 6);
412 CHECK_LT(dictionary_hash_postfix
.size(),
413 static_cast<size_t>(input_buffer_size
));
414 memcpy(input_buffer
, dictionary_hash_postfix
.data(),
415 dictionary_hash_postfix
.size());
416 filter
->FlushStreamBuffer(dictionary_hash_postfix
.size());
418 // With a non-existant dictionary specifier, try to read output.
419 output_bytes_or_buffer_size
= sizeof(output_buffer
);
420 status
= filter
->ReadData(output_buffer
, &output_bytes_or_buffer_size
);
422 EXPECT_EQ(0, output_bytes_or_buffer_size
);
423 EXPECT_EQ(Filter::FILTER_ERROR
, status
);
425 EXPECT_EQ(SDCH_DOMAIN_BLACKLIST_INCLUDES_TARGET
,
426 sdch_manager_
->IsInSupportedDomain(GURL(url_string
)));
427 sdch_manager_
->ClearBlacklistings();
428 EXPECT_EQ(SDCH_OK
, sdch_manager_
->IsInSupportedDomain(GURL(url_string
)));
431 TEST_F(SdchFilterTest
, DictionaryAddOnce
) {
432 // Construct a valid SDCH dictionary from a VCDIFF dictionary.
433 const std::string kSampleDomain
= "sdchtest.com";
434 std::string
dictionary(NewSdchDictionary(kSampleDomain
));
436 std::string url_string
= "http://" + kSampleDomain
;
437 GURL
url(url_string
);
438 EXPECT_TRUE(AddSdchDictionary(dictionary
, url
));
440 // Check we can't add it twice.
441 EXPECT_FALSE(AddSdchDictionary(dictionary
, url
));
443 const std::string kSampleDomain2
= "sdchtest2.com";
445 // Construct a second SDCH dictionary from a VCDIFF dictionary.
446 std::string
dictionary2(NewSdchDictionary(kSampleDomain2
));
448 std::string url_string2
= "http://" + kSampleDomain2
;
449 GURL
url2(url_string2
);
450 EXPECT_TRUE(AddSdchDictionary(dictionary2
, url2
));
453 TEST_F(SdchFilterTest
, BasicDictionary
) {
454 // Construct a valid SDCH dictionary from a VCDIFF dictionary.
455 const std::string kSampleDomain
= "sdchtest.com";
456 std::string
dictionary(NewSdchDictionary(kSampleDomain
));
458 std::string url_string
= "http://" + kSampleDomain
;
460 GURL
url(url_string
);
461 EXPECT_TRUE(AddSdchDictionary(dictionary
, url
));
463 std::string
compressed(NewSdchCompressedData(dictionary
));
465 std::vector
<Filter::FilterType
> filter_types
;
466 filter_types
.push_back(Filter::FILTER_TYPE_SDCH
);
468 SetupFilterContextWithGURL(url
);
470 scoped_ptr
<Filter
> filter(Filter::Factory(filter_types
, *filter_context()));
472 size_t feed_block_size
= 100;
473 size_t output_block_size
= 100;
475 EXPECT_TRUE(FilterTestData(compressed
, feed_block_size
, output_block_size
,
476 filter
.get(), &output
));
477 EXPECT_EQ(output
, expanded_
);
479 // Decode with really small buffers (size 1) to check for edge effects.
480 filter
.reset(Filter::Factory(filter_types
, *filter_context()));
483 output_block_size
= 1;
485 EXPECT_TRUE(FilterTestData(compressed
, feed_block_size
, output_block_size
,
486 filter
.get(), &output
));
487 EXPECT_EQ(output
, expanded_
);
490 TEST_F(SdchFilterTest
, NoDecodeHttps
) {
491 // Construct a valid SDCH dictionary from a VCDIFF dictionary.
492 const std::string kSampleDomain
= "sdchtest.com";
493 std::string
dictionary(NewSdchDictionary(kSampleDomain
));
495 std::string url_string
= "http://" + kSampleDomain
;
497 GURL
url(url_string
);
498 EXPECT_TRUE(AddSdchDictionary(dictionary
, url
));
500 std::string
compressed(NewSdchCompressedData(dictionary
));
502 std::vector
<Filter::FilterType
> filter_types
;
503 filter_types
.push_back(Filter::FILTER_TYPE_SDCH
);
505 GURL
filter_context_gurl("https://" + kSampleDomain
);
506 SetupFilterContextWithGURL(GURL("https://" + kSampleDomain
));
507 scoped_ptr
<Filter
> filter(Filter::Factory(filter_types
, *filter_context()));
509 const size_t feed_block_size(100);
510 const size_t output_block_size(100);
513 EXPECT_FALSE(FilterTestData(compressed
, feed_block_size
, output_block_size
,
514 filter
.get(), &output
));
517 // Current failsafe TODO/hack refuses to decode any content that doesn't use
518 // http as the scheme (see use of DICTIONARY_SELECTED_FOR_NON_HTTP).
519 // The following tests this blockage. Note that blacklisting results, so we
520 // we need separate tests for each of these.
521 TEST_F(SdchFilterTest
, NoDecodeFtp
) {
522 // Construct a valid SDCH dictionary from a VCDIFF dictionary.
523 const std::string kSampleDomain
= "sdchtest.com";
524 std::string
dictionary(NewSdchDictionary(kSampleDomain
));
526 std::string url_string
= "http://" + kSampleDomain
;
528 GURL
url(url_string
);
529 EXPECT_TRUE(AddSdchDictionary(dictionary
, url
));
531 std::string
compressed(NewSdchCompressedData(dictionary
));
533 std::vector
<Filter::FilterType
> filter_types
;
534 filter_types
.push_back(Filter::FILTER_TYPE_SDCH
);
536 SetupFilterContextWithGURL(GURL("ftp://" + kSampleDomain
));
537 scoped_ptr
<Filter
> filter(Filter::Factory(filter_types
, *filter_context()));
539 const size_t feed_block_size(100);
540 const size_t output_block_size(100);
543 EXPECT_FALSE(FilterTestData(compressed
, feed_block_size
, output_block_size
,
544 filter
.get(), &output
));
547 TEST_F(SdchFilterTest
, NoDecodeFileColon
) {
548 // Construct a valid SDCH dictionary from a VCDIFF dictionary.
549 const std::string kSampleDomain
= "sdchtest.com";
550 std::string
dictionary(NewSdchDictionary(kSampleDomain
));
552 std::string url_string
= "http://" + kSampleDomain
;
554 GURL
url(url_string
);
555 EXPECT_TRUE(AddSdchDictionary(dictionary
, url
));
557 std::string
compressed(NewSdchCompressedData(dictionary
));
559 std::vector
<Filter::FilterType
> filter_types
;
560 filter_types
.push_back(Filter::FILTER_TYPE_SDCH
);
562 SetupFilterContextWithGURL(GURL("file://" + kSampleDomain
));
563 scoped_ptr
<Filter
> filter(Filter::Factory(filter_types
, *filter_context()));
565 const size_t feed_block_size(100);
566 const size_t output_block_size(100);
569 EXPECT_FALSE(FilterTestData(compressed
, feed_block_size
, output_block_size
,
570 filter
.get(), &output
));
573 TEST_F(SdchFilterTest
, NoDecodeAboutColon
) {
574 // Construct a valid SDCH dictionary from a VCDIFF dictionary.
575 const std::string kSampleDomain
= "sdchtest.com";
576 std::string
dictionary(NewSdchDictionary(kSampleDomain
));
578 std::string url_string
= "http://" + kSampleDomain
;
580 GURL
url(url_string
);
581 EXPECT_TRUE(AddSdchDictionary(dictionary
, url
));
583 std::string
compressed(NewSdchCompressedData(dictionary
));
585 std::vector
<Filter::FilterType
> filter_types
;
586 filter_types
.push_back(Filter::FILTER_TYPE_SDCH
);
588 SetupFilterContextWithGURL(GURL("about://" + kSampleDomain
));
589 scoped_ptr
<Filter
> filter(Filter::Factory(filter_types
, *filter_context()));
591 const size_t feed_block_size(100);
592 const size_t output_block_size(100);
595 EXPECT_FALSE(FilterTestData(compressed
, feed_block_size
, output_block_size
,
596 filter
.get(), &output
));
599 TEST_F(SdchFilterTest
, NoDecodeJavaScript
) {
600 // Construct a valid SDCH dictionary from a VCDIFF dictionary.
601 const std::string kSampleDomain
= "sdchtest.com";
602 std::string
dictionary(NewSdchDictionary(kSampleDomain
));
604 std::string url_string
= "http://" + kSampleDomain
;
606 GURL
url(url_string
);
607 EXPECT_TRUE(AddSdchDictionary(dictionary
, url
));
609 std::string
compressed(NewSdchCompressedData(dictionary
));
611 std::vector
<Filter::FilterType
> filter_types
;
612 filter_types
.push_back(Filter::FILTER_TYPE_SDCH
);
614 SetupFilterContextWithGURL(GURL("javascript://" + kSampleDomain
));
615 scoped_ptr
<Filter
> filter(Filter::Factory(filter_types
, *filter_context()));
617 const size_t feed_block_size(100);
618 const size_t output_block_size(100);
621 EXPECT_FALSE(FilterTestData(compressed
, feed_block_size
, output_block_size
,
622 filter
.get(), &output
));
625 TEST_F(SdchFilterTest
, CanStillDecodeHttp
) {
626 // Construct a valid SDCH dictionary from a VCDIFF dictionary.
627 const std::string kSampleDomain
= "sdchtest.com";
628 std::string
dictionary(NewSdchDictionary(kSampleDomain
));
630 std::string url_string
= "http://" + kSampleDomain
;
632 GURL
url(url_string
);
633 EXPECT_TRUE(AddSdchDictionary(dictionary
, url
));
635 std::string
compressed(NewSdchCompressedData(dictionary
));
637 std::vector
<Filter::FilterType
> filter_types
;
638 filter_types
.push_back(Filter::FILTER_TYPE_SDCH
);
640 SetupFilterContextWithGURL(GURL("http://" + kSampleDomain
));
641 scoped_ptr
<Filter
> filter(Filter::Factory(filter_types
, *filter_context()));
643 const size_t feed_block_size(100);
644 const size_t output_block_size(100);
647 EXPECT_TRUE(FilterTestData(compressed
, feed_block_size
, output_block_size
,
648 filter
.get(), &output
));
651 TEST_F(SdchFilterTest
, CrossDomainDictionaryUse
) {
652 // Construct a valid SDCH dictionary from a VCDIFF dictionary.
653 const std::string kSampleDomain
= "sdchtest.com";
654 std::string
dictionary(NewSdchDictionary(kSampleDomain
));
656 std::string url_string
= "http://" + kSampleDomain
;
658 GURL
url(url_string
);
659 EXPECT_TRUE(AddSdchDictionary(dictionary
, url
));
661 std::string
compressed(NewSdchCompressedData(dictionary
));
663 std::vector
<Filter::FilterType
> filter_types
;
664 filter_types
.push_back(Filter::FILTER_TYPE_SDCH
);
666 // Decode with content arriving from the "wrong" domain.
667 // This tests SdchManager::CanSet().
668 GURL
wrong_domain_url("http://www.wrongdomain.com");
669 SetupFilterContextWithGURL(wrong_domain_url
);
670 scoped_ptr
<Filter
> filter(Filter::Factory(filter_types
, *filter_context()));
672 size_t feed_block_size
= 100;
673 size_t output_block_size
= 100;
675 EXPECT_FALSE(FilterTestData(compressed
, feed_block_size
, output_block_size
,
676 filter
.get(), &output
));
677 EXPECT_EQ(output
.size(), 0u); // No output written.
679 EXPECT_EQ(SDCH_OK
, sdch_manager_
->IsInSupportedDomain(GURL(url_string
)));
680 EXPECT_EQ(SDCH_DOMAIN_BLACKLIST_INCLUDES_TARGET
,
681 sdch_manager_
->IsInSupportedDomain(wrong_domain_url
));
682 sdch_manager_
->ClearBlacklistings();
683 EXPECT_EQ(SDCH_OK
, sdch_manager_
->IsInSupportedDomain(wrong_domain_url
));
686 TEST_F(SdchFilterTest
, DictionaryPathValidation
) {
687 // Construct a valid SDCH dictionary from a VCDIFF dictionary.
688 const std::string kSampleDomain
= "sdchtest.com";
689 std::string
dictionary(NewSdchDictionary(kSampleDomain
));
691 std::string url_string
= "http://" + kSampleDomain
;
693 GURL
url(url_string
);
694 EXPECT_TRUE(AddSdchDictionary(dictionary
, url
));
696 // Create a dictionary with a path restriction, by prefixing dictionary.
697 const std::string
path("/special_path/bin");
698 std::string
dictionary_with_path("Path: " + path
+ "\n");
699 dictionary_with_path
.append(dictionary
);
700 GURL
url2(url_string
+ path
);
701 EXPECT_TRUE(AddSdchDictionary(dictionary_with_path
, url2
));
703 std::string
compressed_for_path(NewSdchCompressedData(dictionary_with_path
));
705 std::vector
<Filter::FilterType
> filter_types
;
706 filter_types
.push_back(Filter::FILTER_TYPE_SDCH
);
708 // Test decode the path data, arriving from a valid path.
709 SetupFilterContextWithGURL(GURL(url_string
+ path
));
710 scoped_ptr
<Filter
> filter(Filter::Factory(filter_types
, *filter_context()));
712 size_t feed_block_size
= 100;
713 size_t output_block_size
= 100;
716 EXPECT_TRUE(FilterTestData(compressed_for_path
, feed_block_size
,
717 output_block_size
, filter
.get(), &output
));
718 EXPECT_EQ(output
, expanded_
);
720 // Test decode the path data, arriving from a invalid path.
721 SetupFilterContextWithGURL(GURL(url_string
));
722 filter
.reset(Filter::Factory(filter_types
, *filter_context()));
724 feed_block_size
= 100;
725 output_block_size
= 100;
727 EXPECT_FALSE(FilterTestData(compressed_for_path
, feed_block_size
,
728 output_block_size
, filter
.get(), &output
));
729 EXPECT_EQ(output
.size(), 0u); // No output written.
731 EXPECT_EQ(SDCH_DOMAIN_BLACKLIST_INCLUDES_TARGET
,
732 sdch_manager_
->IsInSupportedDomain(GURL(url_string
)));
733 sdch_manager_
->ClearBlacklistings();
734 EXPECT_EQ(SDCH_OK
, sdch_manager_
->IsInSupportedDomain(GURL(url_string
)));
737 TEST_F(SdchFilterTest
, DictionaryPortValidation
) {
738 // Construct a valid SDCH dictionary from a VCDIFF dictionary.
739 const std::string kSampleDomain
= "sdchtest.com";
740 std::string
dictionary(NewSdchDictionary(kSampleDomain
));
742 std::string url_string
= "http://" + kSampleDomain
;
744 GURL
url(url_string
);
745 EXPECT_TRUE(AddSdchDictionary(dictionary
, url
));
747 // Create a dictionary with a port restriction, by prefixing old dictionary.
748 const std::string
port("502");
749 std::string
dictionary_with_port("Port: " + port
+ "\n");
750 dictionary_with_port
.append("Port: 80\n"); // Add default port.
751 dictionary_with_port
.append(dictionary
);
752 EXPECT_TRUE(AddSdchDictionary(dictionary_with_port
,
753 GURL(url_string
+ ":" + port
)));
755 std::string
compressed_for_port(NewSdchCompressedData(dictionary_with_port
));
757 std::vector
<Filter::FilterType
> filter_types
;
758 filter_types
.push_back(Filter::FILTER_TYPE_SDCH
);
760 // Test decode the port data, arriving from a valid port.
761 SetupFilterContextWithGURL(GURL(url_string
+ ":" + port
));
762 scoped_ptr
<Filter
> filter(Filter::Factory(filter_types
, *filter_context()));
764 size_t feed_block_size
= 100;
765 size_t output_block_size
= 100;
767 EXPECT_TRUE(FilterTestData(compressed_for_port
, feed_block_size
,
768 output_block_size
, filter
.get(), &output
));
769 EXPECT_EQ(output
, expanded_
);
771 // Test decode the port data, arriving from a valid (default) port.
772 SetupFilterContextWithGURL(GURL(url_string
)); // Default port.
773 filter
.reset(Filter::Factory(filter_types
, *filter_context()));
775 feed_block_size
= 100;
776 output_block_size
= 100;
778 EXPECT_TRUE(FilterTestData(compressed_for_port
, feed_block_size
,
779 output_block_size
, filter
.get(), &output
));
780 EXPECT_EQ(output
, expanded_
);
782 // Test decode the port data, arriving from a invalid port.
783 SetupFilterContextWithGURL(GURL(url_string
+ ":" + port
+ "1"));
784 filter
.reset(Filter::Factory(filter_types
, *filter_context()));
786 feed_block_size
= 100;
787 output_block_size
= 100;
789 EXPECT_FALSE(FilterTestData(compressed_for_port
, feed_block_size
,
790 output_block_size
, filter
.get(), &output
));
791 EXPECT_EQ(output
.size(), 0u); // No output written.
793 EXPECT_EQ(SDCH_DOMAIN_BLACKLIST_INCLUDES_TARGET
,
794 sdch_manager_
->IsInSupportedDomain(GURL(url_string
)));
795 sdch_manager_
->ClearBlacklistings();
796 EXPECT_EQ(SDCH_OK
, sdch_manager_
->IsInSupportedDomain(GURL(url_string
)));
799 // Helper function to perform gzip compression of data.
800 static std::string
gzip_compress(const std::string
&input
) {
801 z_stream zlib_stream
;
802 memset(&zlib_stream
, 0, sizeof(zlib_stream
));
806 code
= deflateInit2(&zlib_stream
, Z_DEFAULT_COMPRESSION
, Z_DEFLATED
,
811 CHECK_EQ(Z_OK
, code
);
813 // Fill in zlib control block
814 zlib_stream
.next_in
= bit_cast
<Bytef
*>(input
.data());
815 zlib_stream
.avail_in
= input
.size();
817 // Assume we can compress into similar buffer (add 100 bytes to be sure).
818 size_t gzip_compressed_length
= zlib_stream
.avail_in
+ 100;
819 scoped_ptr
<char[]> gzip_compressed(new char[gzip_compressed_length
]);
820 zlib_stream
.next_out
= bit_cast
<Bytef
*>(gzip_compressed
.get());
821 zlib_stream
.avail_out
= gzip_compressed_length
;
823 // The GZIP header (see RFC 1952):
824 // +---+---+---+---+---+---+---+---+---+---+
825 // |ID1|ID2|CM |FLG| MTIME |XFL|OS |
826 // +---+---+---+---+---+---+---+---+---+---+
829 // CM \010 (compression method == DEFLATE)
830 // FLG \000 (special flags that we do not support)
831 // MTIME Unix format modification time (0 means not available)
832 // XFL 2-4? DEFLATE flags
833 // OS ???? Operating system indicator (255 means unknown)
835 // Header value we generate:
836 const char kGZipHeader
[] = { '\037', '\213', '\010', '\000', '\000',
837 '\000', '\000', '\000', '\002', '\377' };
838 CHECK_GT(zlib_stream
.avail_out
, sizeof(kGZipHeader
));
839 memcpy(zlib_stream
.next_out
, kGZipHeader
, sizeof(kGZipHeader
));
840 zlib_stream
.next_out
+= sizeof(kGZipHeader
);
841 zlib_stream
.avail_out
-= sizeof(kGZipHeader
);
844 code
= deflate(&zlib_stream
, Z_FINISH
);
845 gzip_compressed_length
-= zlib_stream
.avail_out
;
846 std::string
compressed(gzip_compressed
.get(), gzip_compressed_length
);
847 deflateEnd(&zlib_stream
);
851 //------------------------------------------------------------------------------
853 class SdchFilterChainingTest
{
855 static Filter
* Factory(const std::vector
<Filter::FilterType
>& types
,
856 const FilterContext
& context
, int size
) {
857 return Filter::FactoryForTests(types
, context
, size
);
861 // Test that filters can be cascaded (chained) so that the output of one filter
862 // is processed by the next one. This is most critical for SDCH, which is
863 // routinely followed by gzip (during encoding). The filter we'll test for will
864 // do the gzip decoding first, and then decode the SDCH content.
865 TEST_F(SdchFilterTest
, FilterChaining
) {
866 // Construct a valid SDCH dictionary from a VCDIFF dictionary.
867 const std::string kSampleDomain
= "sdchtest.com";
868 std::string
dictionary(NewSdchDictionary(kSampleDomain
));
870 std::string url_string
= "http://" + kSampleDomain
;
872 GURL
url(url_string
);
873 EXPECT_TRUE(AddSdchDictionary(dictionary
, url
));
875 std::string
sdch_compressed(NewSdchCompressedData(dictionary
));
877 // Use Gzip to compress the sdch sdch_compressed data.
878 std::string gzip_compressed_sdch
= gzip_compress(sdch_compressed
);
880 // Construct a chained filter.
881 std::vector
<Filter::FilterType
> filter_types
;
882 filter_types
.push_back(Filter::FILTER_TYPE_SDCH
);
883 filter_types
.push_back(Filter::FILTER_TYPE_GZIP
);
885 // First try with a large buffer (larger than test input, or compressed data).
886 const size_t kLargeInputBufferSize(1000); // Used internally in filters.
887 CHECK_GT(kLargeInputBufferSize
, gzip_compressed_sdch
.size());
888 CHECK_GT(kLargeInputBufferSize
, sdch_compressed
.size());
889 CHECK_GT(kLargeInputBufferSize
, expanded_
.size());
890 SetupFilterContextWithGURL(url
);
891 scoped_ptr
<Filter
> filter(
892 SdchFilterChainingTest::Factory(filter_types
, *filter_context(),
893 kLargeInputBufferSize
));
894 EXPECT_EQ(static_cast<int>(kLargeInputBufferSize
),
895 filter
->stream_buffer_size());
897 // Verify that chained filter is waiting for data.
898 char tiny_output_buffer
[10];
899 int tiny_output_size
= sizeof(tiny_output_buffer
);
900 EXPECT_EQ(Filter::FILTER_NEED_MORE_DATA
,
901 filter
->ReadData(tiny_output_buffer
, &tiny_output_size
));
903 // Make chain process all data.
904 size_t feed_block_size
= kLargeInputBufferSize
;
905 size_t output_block_size
= kLargeInputBufferSize
;
907 EXPECT_TRUE(FilterTestData(gzip_compressed_sdch
, feed_block_size
,
908 output_block_size
, filter
.get(), &output
));
909 EXPECT_EQ(output
, expanded_
);
911 // Next try with a mid-sized internal buffer size.
912 const size_t kMidSizedInputBufferSize(100);
913 // Buffer should be big enough to swallow whole gzip content.
914 CHECK_GT(kMidSizedInputBufferSize
, gzip_compressed_sdch
.size());
915 // Buffer should be small enough that entire SDCH content can't fit.
916 // We'll go even further, and force the chain to flush the buffer between the
917 // two filters more than once (that is why we multiply by 2).
918 CHECK_LT(kMidSizedInputBufferSize
* 2, sdch_compressed
.size());
919 filter_context()->SetURL(url
);
921 SdchFilterChainingTest::Factory(filter_types
, *filter_context(),
922 kMidSizedInputBufferSize
));
923 EXPECT_EQ(static_cast<int>(kMidSizedInputBufferSize
),
924 filter
->stream_buffer_size());
926 feed_block_size
= kMidSizedInputBufferSize
;
927 output_block_size
= kMidSizedInputBufferSize
;
929 EXPECT_TRUE(FilterTestData(gzip_compressed_sdch
, feed_block_size
,
930 output_block_size
, filter
.get(), &output
));
931 EXPECT_EQ(output
, expanded_
);
933 // Next try with a tiny input and output buffer to cover edge effects.
934 filter
.reset(SdchFilterChainingTest::Factory(filter_types
, *filter_context(),
935 kLargeInputBufferSize
));
936 EXPECT_EQ(static_cast<int>(kLargeInputBufferSize
),
937 filter
->stream_buffer_size());
940 output_block_size
= 1;
942 EXPECT_TRUE(FilterTestData(gzip_compressed_sdch
, feed_block_size
,
943 output_block_size
, filter
.get(), &output
));
944 EXPECT_EQ(output
, expanded_
);
947 TEST_F(SdchFilterTest
, DefaultGzipIfSdch
) {
948 // Construct a valid SDCH dictionary from a VCDIFF dictionary.
949 const std::string kSampleDomain
= "sdchtest.com";
950 std::string
dictionary(NewSdchDictionary(kSampleDomain
));
952 std::string url_string
= "http://" + kSampleDomain
;
954 GURL
url(url_string
);
955 EXPECT_TRUE(AddSdchDictionary(dictionary
, url
));
957 std::string
sdch_compressed(NewSdchCompressedData(dictionary
));
959 // Use Gzip to compress the sdch sdch_compressed data.
960 std::string gzip_compressed_sdch
= gzip_compress(sdch_compressed
);
962 // Only claim to have sdch content, but really use the gzipped sdch content.
963 // System should automatically add the missing (optional) gzip.
964 std::vector
<Filter::FilterType
> filter_types
;
965 filter_types
.push_back(Filter::FILTER_TYPE_SDCH
);
967 filter_context()->SetMimeType("anything/mime");
968 SetupFilterContextWithGURL(url
);
970 Filter::FixupEncodingTypes(*filter_context(), &filter_types
);
971 ASSERT_EQ(filter_types
.size(), 2u);
972 EXPECT_EQ(filter_types
[0], Filter::FILTER_TYPE_SDCH
);
973 EXPECT_EQ(filter_types
[1], Filter::FILTER_TYPE_GZIP_HELPING_SDCH
);
975 // First try with a large buffer (larger than test input, or compressed data).
976 scoped_ptr
<Filter
> filter(Filter::Factory(filter_types
, *filter_context()));
978 // Verify that chained filter is waiting for data.
979 char tiny_output_buffer
[10];
980 int tiny_output_size
= sizeof(tiny_output_buffer
);
981 EXPECT_EQ(Filter::FILTER_NEED_MORE_DATA
,
982 filter
->ReadData(tiny_output_buffer
, &tiny_output_size
));
984 size_t feed_block_size
= 100;
985 size_t output_block_size
= 100;
987 EXPECT_TRUE(FilterTestData(gzip_compressed_sdch
, feed_block_size
,
988 output_block_size
, filter
.get(), &output
));
989 EXPECT_EQ(output
, expanded_
);
991 // Next try with a tiny buffer to cover edge effects.
992 filter
.reset(Filter::Factory(filter_types
, *filter_context()));
995 output_block_size
= 1;
997 EXPECT_TRUE(FilterTestData(gzip_compressed_sdch
, feed_block_size
,
998 output_block_size
, filter
.get(), &output
));
999 EXPECT_EQ(output
, expanded_
);
1002 TEST_F(SdchFilterTest
, AcceptGzipSdchIfGzip
) {
1003 // Construct a valid SDCH dictionary from a VCDIFF dictionary.
1004 const std::string kSampleDomain
= "sdchtest.com";
1005 std::string
dictionary(NewSdchDictionary(kSampleDomain
));
1007 std::string url_string
= "http://" + kSampleDomain
;
1009 GURL
url(url_string
);
1010 EXPECT_TRUE(AddSdchDictionary(dictionary
, url
));
1012 std::string
sdch_compressed(NewSdchCompressedData(dictionary
));
1014 // Use Gzip to compress the sdch sdch_compressed data.
1015 std::string gzip_compressed_sdch
= gzip_compress(sdch_compressed
);
1017 // Some proxies strip the content encoding statement down to a mere gzip, but
1018 // pass through the original content (with full sdch,gzip encoding).
1019 // Only claim to have gzip content, but really use the gzipped sdch content.
1020 // System should automatically add the missing (optional) sdch.
1021 std::vector
<Filter::FilterType
> filter_types
;
1022 filter_types
.push_back(Filter::FILTER_TYPE_GZIP
);
1024 filter_context()->SetMimeType("anything/mime");
1025 SetupFilterContextWithGURL(url
);
1026 Filter::FixupEncodingTypes(*filter_context(), &filter_types
);
1027 ASSERT_EQ(filter_types
.size(), 3u);
1028 EXPECT_EQ(filter_types
[0], Filter::FILTER_TYPE_SDCH_POSSIBLE
);
1029 EXPECT_EQ(filter_types
[1], Filter::FILTER_TYPE_GZIP_HELPING_SDCH
);
1030 EXPECT_EQ(filter_types
[2], Filter::FILTER_TYPE_GZIP
);
1032 // First try with a large buffer (larger than test input, or compressed data).
1033 scoped_ptr
<Filter
> filter(Filter::Factory(filter_types
, *filter_context()));
1035 // Verify that chained filter is waiting for data.
1036 char tiny_output_buffer
[10];
1037 int tiny_output_size
= sizeof(tiny_output_buffer
);
1038 EXPECT_EQ(Filter::FILTER_NEED_MORE_DATA
,
1039 filter
->ReadData(tiny_output_buffer
, &tiny_output_size
));
1041 size_t feed_block_size
= 100;
1042 size_t output_block_size
= 100;
1044 EXPECT_TRUE(FilterTestData(gzip_compressed_sdch
, feed_block_size
,
1045 output_block_size
, filter
.get(), &output
));
1046 EXPECT_EQ(output
, expanded_
);
1048 // Next try with a tiny buffer to cover edge effects.
1049 filter
.reset(Filter::Factory(filter_types
, *filter_context()));
1051 feed_block_size
= 1;
1052 output_block_size
= 1;
1054 EXPECT_TRUE(FilterTestData(gzip_compressed_sdch
, feed_block_size
,
1055 output_block_size
, filter
.get(), &output
));
1056 EXPECT_EQ(output
, expanded_
);
1059 TEST_F(SdchFilterTest
, DefaultSdchGzipIfEmpty
) {
1060 // Construct a valid SDCH dictionary from a VCDIFF dictionary.
1061 const std::string kSampleDomain
= "sdchtest.com";
1062 std::string
dictionary(NewSdchDictionary(kSampleDomain
));
1064 std::string url_string
= "http://" + kSampleDomain
;
1066 GURL
url(url_string
);
1067 EXPECT_TRUE(AddSdchDictionary(dictionary
, url
));
1069 std::string
sdch_compressed(NewSdchCompressedData(dictionary
));
1071 // Use Gzip to compress the sdch sdch_compressed data.
1072 std::string gzip_compressed_sdch
= gzip_compress(sdch_compressed
);
1074 // Only claim to have non-encoded content, but really use the gzipped sdch
1076 // System should automatically add the missing (optional) sdch,gzip.
1077 std::vector
<Filter::FilterType
> filter_types
;
1079 filter_context()->SetMimeType("anything/mime");
1080 SetupFilterContextWithGURL(url
);
1081 Filter::FixupEncodingTypes(*filter_context(), &filter_types
);
1082 ASSERT_EQ(filter_types
.size(), 2u);
1083 EXPECT_EQ(filter_types
[0], Filter::FILTER_TYPE_SDCH_POSSIBLE
);
1084 EXPECT_EQ(filter_types
[1], Filter::FILTER_TYPE_GZIP_HELPING_SDCH
);
1086 // First try with a large buffer (larger than test input, or compressed data).
1087 scoped_ptr
<Filter
> filter(Filter::Factory(filter_types
, *filter_context()));
1089 // Verify that chained filter is waiting for data.
1090 char tiny_output_buffer
[10];
1091 int tiny_output_size
= sizeof(tiny_output_buffer
);
1092 EXPECT_EQ(Filter::FILTER_NEED_MORE_DATA
,
1093 filter
->ReadData(tiny_output_buffer
, &tiny_output_size
));
1095 size_t feed_block_size
= 100;
1096 size_t output_block_size
= 100;
1098 EXPECT_TRUE(FilterTestData(gzip_compressed_sdch
, feed_block_size
,
1099 output_block_size
, filter
.get(), &output
));
1100 EXPECT_EQ(output
, expanded_
);
1102 // Next try with a tiny buffer to cover edge effects.
1103 filter
.reset(Filter::Factory(filter_types
, *filter_context()));
1105 feed_block_size
= 1;
1106 output_block_size
= 1;
1108 EXPECT_TRUE(FilterTestData(gzip_compressed_sdch
, feed_block_size
,
1109 output_block_size
, filter
.get(), &output
));
1110 EXPECT_EQ(output
, expanded_
);
1113 TEST_F(SdchFilterTest
, AcceptGzipGzipSdchIfGzip
) {
1114 // Construct a valid SDCH dictionary from a VCDIFF dictionary.
1115 const std::string kSampleDomain
= "sdchtest.com";
1116 std::string
dictionary(NewSdchDictionary(kSampleDomain
));
1118 std::string url_string
= "http://" + kSampleDomain
;
1120 GURL
url(url_string
);
1121 EXPECT_TRUE(AddSdchDictionary(dictionary
, url
));
1123 std::string
sdch_compressed(NewSdchCompressedData(dictionary
));
1125 // Vodaphone (UK) Mobile Broadband provides double gzipped sdch with a content
1126 // encoding of merely gzip (apparently, only listing the extra level of
1127 // wrapper compression they added, but discarding the actual content encoding.
1128 // Use Gzip to double compress the sdch sdch_compressed data.
1129 std::string double_gzip_compressed_sdch
= gzip_compress(gzip_compress(
1132 // Only claim to have gzip content, but really use the double gzipped sdch
1134 // System should automatically add the missing (optional) sdch, gzip decoders.
1135 std::vector
<Filter::FilterType
> filter_types
;
1136 filter_types
.push_back(Filter::FILTER_TYPE_GZIP
);
1138 filter_context()->SetMimeType("anything/mime");
1139 SetupFilterContextWithGURL(url
);
1140 Filter::FixupEncodingTypes(*filter_context(), &filter_types
);
1141 ASSERT_EQ(filter_types
.size(), 3u);
1142 EXPECT_EQ(filter_types
[0], Filter::FILTER_TYPE_SDCH_POSSIBLE
);
1143 EXPECT_EQ(filter_types
[1], Filter::FILTER_TYPE_GZIP_HELPING_SDCH
);
1144 EXPECT_EQ(filter_types
[2], Filter::FILTER_TYPE_GZIP
);
1146 // First try with a large buffer (larger than test input, or compressed data).
1147 scoped_ptr
<Filter
> filter(Filter::Factory(filter_types
, *filter_context()));
1149 // Verify that chained filter is waiting for data.
1150 char tiny_output_buffer
[10];
1151 int tiny_output_size
= sizeof(tiny_output_buffer
);
1152 EXPECT_EQ(Filter::FILTER_NEED_MORE_DATA
,
1153 filter
->ReadData(tiny_output_buffer
, &tiny_output_size
));
1155 size_t feed_block_size
= 100;
1156 size_t output_block_size
= 100;
1158 EXPECT_TRUE(FilterTestData(double_gzip_compressed_sdch
, feed_block_size
,
1159 output_block_size
, filter
.get(), &output
));
1160 EXPECT_EQ(output
, expanded_
);
1162 // Next try with a tiny buffer to cover edge effects.
1163 filter
.reset(Filter::Factory(filter_types
, *filter_context()));
1165 feed_block_size
= 1;
1166 output_block_size
= 1;
1168 EXPECT_TRUE(FilterTestData(double_gzip_compressed_sdch
, feed_block_size
,
1169 output_block_size
, filter
.get(), &output
));
1170 EXPECT_EQ(output
, expanded_
);
1173 // Test to make sure we decode properly with an unexpected dictionary.
1174 TEST_F(SdchFilterTest
, UnexpectedDictionary
) {
1175 // Setup a dictionary, add it to the filter context, and create a filter
1176 // based on that dictionary.
1177 const std::string kSampleDomain
= "sdchtest.com";
1178 std::string
dictionary(NewSdchDictionary(kSampleDomain
));
1179 std::string url_string
= "http://" + kSampleDomain
;
1180 GURL
url(url_string
);
1181 EXPECT_TRUE(AddSdchDictionary(dictionary
, url
));
1183 SetupFilterContextWithGURL(url
);
1185 std::vector
<Filter::FilterType
> filter_types
;
1186 filter_types
.push_back(Filter::FILTER_TYPE_SDCH
);
1187 scoped_ptr
<Filter
> filter(Filter::Factory(filter_types
, *filter_context()));
1189 // Setup another dictionary, expired. Don't add it to the filter context.
1190 // Delete stored dictionaries first to handle platforms which only
1191 // have room for a single dictionary.
1192 sdch_manager_
->ClearData();
1193 std::string
expired_dictionary(NewSdchExpiredDictionary(kSampleDomain
));
1195 // Don't use the Helper function since its insertion check is indeterminate
1196 // for a Max-Age: 0 dictionary.
1197 sdch_manager_
->AddSdchDictionary(expired_dictionary
, url
, nullptr);
1199 std::string client_hash
;
1200 std::string server_hash
;
1201 SdchManager::GenerateHash(expired_dictionary
, &client_hash
, &server_hash
);
1203 // Make sure Max-Age: 0 shows up as expired.
1204 scoped_ptr
<base::SimpleTestClock
> clock(new base::SimpleTestClock
);
1205 clock
->SetNow(base::Time::Now());
1206 clock
->Advance(base::TimeDelta::FromMinutes(5));
1207 SdchProblemCode problem_code
;
1208 scoped_ptr
<SdchManager::DictionarySet
> hash_set(
1209 sdch_manager_
->GetDictionarySetByHash(
1210 url
, server_hash
, &problem_code
).Pass());
1211 ASSERT_TRUE(hash_set
);
1212 ASSERT_EQ(SDCH_OK
, problem_code
);
1214 const_cast<SdchManager::Dictionary
*>(
1215 hash_set
->GetDictionary(server_hash
))->SetClockForTesting(
1218 // Encode output with the second dictionary.
1219 std::string
sdch_compressed(NewSdchCompressedData(expired_dictionary
));
1221 // See if the filter decodes it.
1223 EXPECT_TRUE(FilterTestData(sdch_compressed
, 100, 100, filter
.get(), &output
));
1224 EXPECT_EQ(expanded_
, output
);
1227 class SimpleSdchObserver
: public SdchObserver
{
1229 explicit SimpleSdchObserver(SdchManager
* manager
)
1230 : dictionary_used_(0), manager_(manager
) {
1231 manager_
->AddObserver(this);
1233 ~SimpleSdchObserver() override
{ manager_
->RemoveObserver(this); }
1236 void OnDictionaryUsed(SdchManager
* manager
,
1237 const std::string
& server_hash
) override
{
1239 last_server_hash_
= server_hash
;
1242 int dictionary_used_calls() const { return dictionary_used_
; }
1243 std::string
last_server_hash() const { return last_server_hash_
; }
1245 void OnGetDictionary(SdchManager
* /* manager */,
1246 const GURL
& /* request_url */,
1247 const GURL
& /* dictionary_url */) override
{}
1248 void OnClearDictionaries(SdchManager
* /* manager */) override
{}
1251 int dictionary_used_
;
1252 std::string last_server_hash_
;
1253 SdchManager
* manager_
;
1255 DISALLOW_COPY_AND_ASSIGN(SimpleSdchObserver
);
1258 TEST_F(SdchFilterTest
, DictionaryUsedSignaled
) {
1259 // Construct a valid SDCH dictionary from a VCDIFF dictionary.
1260 const std::string kSampleDomain
= "sdchtest.com";
1261 std::string
dictionary(NewSdchDictionary(kSampleDomain
));
1262 SimpleSdchObserver
observer(sdch_manager_
.get());
1264 std::string url_string
= "http://" + kSampleDomain
;
1266 GURL
url(url_string
);
1267 EXPECT_TRUE(AddSdchDictionary(dictionary
, url
));
1269 std::string client_hash
;
1270 std::string server_hash
;
1271 SdchManager::GenerateHash(dictionary
, &client_hash
, &server_hash
);
1273 std::string
compressed(NewSdchCompressedData(dictionary
));
1275 std::vector
<Filter::FilterType
> filter_types
;
1276 filter_types
.push_back(Filter::FILTER_TYPE_SDCH
);
1278 SetupFilterContextWithGURL(url
);
1280 scoped_ptr
<Filter
> filter(Filter::Factory(filter_types
, *filter_context()));
1282 size_t feed_block_size
= 100;
1283 size_t output_block_size
= 100;
1285 EXPECT_TRUE(FilterTestData(compressed
, feed_block_size
, output_block_size
,
1286 filter
.get(), &output
));
1287 EXPECT_EQ(output
, expanded_
);
1289 filter
.reset(nullptr);
1291 // Confirm that we got a "DictionaryUsed" signal from the SdchManager
1292 // for our dictionary.
1293 EXPECT_EQ(1, observer
.dictionary_used_calls());
1294 EXPECT_EQ(server_hash
, observer
.last_server_hash());