Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(203)

Side by Side Diff: net/filter/sdch_filter_unittest.cc

Issue 2478703004: Remove net::Filter code (Closed)
Patch Set: Address Matt's comment Created 4 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « net/filter/sdch_filter.cc ('k') | net/net.gypi » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
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.
4
5 #include "net/filter/sdch_filter.h"
6
7 #include <limits.h>
8
9 #include <algorithm>
10 #include <memory>
11 #include <string>
12 #include <vector>
13
14 #include "base/bit_cast.h"
15 #include "base/logging.h"
16 #include "base/macros.h"
17 #include "base/test/histogram_tester.h"
18 #include "base/test/simple_test_clock.h"
19 #include "net/base/io_buffer.h"
20 #include "net/base/sdch_dictionary.h"
21 #include "net/base/sdch_manager.h"
22 #include "net/base/sdch_observer.h"
23 #include "net/filter/mock_filter_context.h"
24 #include "net/url_request/url_request_context.h"
25 #include "net/url_request/url_request_http_job.h"
26 #include "testing/gtest/include/gtest/gtest.h"
27 #include "third_party/zlib/zlib.h"
28
29 namespace net {
30
31 //------------------------------------------------------------------------------
32 // Provide sample data and compression results with a sample VCDIFF dictionary.
33 // Note an SDCH dictionary has extra meta-data before the VCDIFF dictionary.
34 static const char kTestVcdiffDictionary[] = "DictionaryFor"
35 "SdchCompression1SdchCompression2SdchCompression3SdchCompression\n";
36 // Pre-compression test data. Note that we pad with a lot of highly gzip
37 // compressible content to help to exercise the chaining pipeline. That is why
38 // there are a PILE of zeros at the start and end.
39 // This will ensure that gzip compressed data can be fed to the chain in one
40 // gulp, but (with careful selection of intermediate buffers) that it takes
41 // several sdch buffers worth of data to satisfy the sdch filter. See detailed
42 // CHECK() calls in FilterChaining test for specifics.
43 static const char kTestData[] = "0000000000000000000000000000000000000000000000"
44 "0000000000000000000000000000TestData "
45 "SdchCompression1SdchCompression2SdchCompression3SdchCompression"
46 "00000000000000000000000000000000000000000000000000000000000000000000000000"
47 "000000000000000000000000000000000000000\n";
48
49 // Note SDCH compressed data will include a reference to the SDCH dictionary.
50 static const char kSdchCompressedTestData[] =
51 "\326\303\304\0\0\001M\0\201S\202\004\0\201E\006\001"
52 "00000000000000000000000000000000000000000000000000000000000000000000000000"
53 "TestData 00000000000000000000000000000000000000000000000000000000000000000"
54 "000000000000000000000000000000000000000000000000\n\001S\023\077\001r\r";
55
56 //------------------------------------------------------------------------------
57
58 class SdchFilterTest : public testing::Test {
59 protected:
60 SdchFilterTest()
61 : test_vcdiff_dictionary_(kTestVcdiffDictionary,
62 sizeof(kTestVcdiffDictionary) - 1),
63 vcdiff_compressed_data_(kSdchCompressedTestData,
64 sizeof(kSdchCompressedTestData) - 1),
65 expanded_(kTestData, sizeof(kTestData) - 1),
66 sdch_manager_(new SdchManager),
67 filter_context_(new MockFilterContext) {
68 URLRequestContext* url_request_context =
69 filter_context_->GetModifiableURLRequestContext();
70
71 url_request_context->set_sdch_manager(sdch_manager_.get());
72 }
73
74 // Attempt to add a dictionary to the manager and probe for success or
75 // failure.
76 bool AddSdchDictionary(const std::string& dictionary_text,
77 const GURL& gurl) {
78 return sdch_manager_->AddSdchDictionary(dictionary_text, gurl, nullptr) ==
79 SDCH_OK;
80 }
81
82 MockFilterContext* filter_context() { return filter_context_.get(); }
83
84 // Sets both the GURL and the SDCH response for a filter context.
85 void SetupFilterContextWithGURL(GURL url) {
86 filter_context_->SetURL(url);
87 filter_context_->SetSdchResponse(sdch_manager_->GetDictionarySet(url));
88 }
89
90 std::string NewSdchCompressedData(const std::string& dictionary) {
91 std::string client_hash;
92 std::string server_hash;
93 SdchManager::GenerateHash(dictionary, &client_hash, &server_hash);
94
95 // Build compressed data that refers to our dictionary.
96 std::string compressed(server_hash);
97 compressed.append("\0", 1);
98 compressed.append(vcdiff_compressed_data_);
99 return compressed;
100 }
101
102 const std::string test_vcdiff_dictionary_;
103 const std::string vcdiff_compressed_data_;
104 const std::string expanded_; // Desired final, decompressed data.
105
106 std::unique_ptr<SdchManager> sdch_manager_;
107 std::unique_ptr<MockFilterContext> filter_context_;
108 };
109
110 TEST_F(SdchFilterTest, Hashing) {
111 std::string client_hash, server_hash;
112 std::string dictionary("test contents");
113 SdchManager::GenerateHash(dictionary, &client_hash, &server_hash);
114
115 EXPECT_EQ(client_hash, "lMQBjS3P");
116 EXPECT_EQ(server_hash, "MyciMVll");
117 }
118
119 //------------------------------------------------------------------------------
120 // Provide a generic helper function for trying to filter data.
121 // This function repeatedly calls the filter to process data, until the entire
122 // source is consumed. The return value from the filter is appended to output.
123 // This allows us to vary input and output block sizes in order to test for edge
124 // effects (boundary effects?) during the filtering process.
125 // This function provides data to the filter in blocks of no-more-than the
126 // specified input_block_length. It allows the filter to fill no more than
127 // output_buffer_length in any one call to proccess (a.k.a., Read) data, and
128 // concatenates all these little output blocks into the singular output string.
129 static bool FilterTestData(const std::string& source,
130 size_t input_block_length,
131 const size_t output_buffer_length,
132 Filter* filter, std::string* output) {
133 CHECK_GT(input_block_length, 0u);
134 Filter::FilterStatus status(Filter::FILTER_NEED_MORE_DATA);
135 size_t source_index = 0;
136 std::unique_ptr<char[]> output_buffer(new char[output_buffer_length]);
137 size_t input_amount = std::min(input_block_length,
138 static_cast<size_t>(filter->stream_buffer_size()));
139
140 do {
141 int copy_amount = std::min(input_amount, source.size() - source_index);
142 if (copy_amount > 0 && status == Filter::FILTER_NEED_MORE_DATA) {
143 memcpy(filter->stream_buffer()->data(), source.data() + source_index,
144 copy_amount);
145 filter->FlushStreamBuffer(copy_amount);
146 source_index += copy_amount;
147 }
148 int buffer_length = output_buffer_length;
149 status = filter->ReadData(output_buffer.get(), &buffer_length);
150 output->append(output_buffer.get(), buffer_length);
151 if (status == Filter::FILTER_ERROR)
152 return false;
153 // Callers assume that FILTER_OK with no output buffer means FILTER_DONE.
154 if (Filter::FILTER_OK == status && 0 == buffer_length)
155 return true;
156 if (copy_amount == 0 && buffer_length == 0)
157 return true;
158 } while (1);
159 }
160
161 static std::string NewSdchDictionary(const std::string& domain) {
162 std::string dictionary;
163 if (!domain.empty()) {
164 dictionary.append("Domain: ");
165 dictionary.append(domain);
166 dictionary.append("\n");
167 }
168 dictionary.append("\n");
169 dictionary.append(kTestVcdiffDictionary, sizeof(kTestVcdiffDictionary) - 1);
170 return dictionary;
171 }
172
173 static std::string NewSdchExpiredDictionary(const std::string& domain) {
174 std::string dictionary;
175 if (!domain.empty()) {
176 dictionary.append("Domain: ");
177 dictionary.append(domain);
178 dictionary.append("\n");
179 }
180 dictionary.append("Max-Age: -1\n");
181 dictionary.append("\n");
182 dictionary.append(kTestVcdiffDictionary, sizeof(kTestVcdiffDictionary) - 1);
183 return dictionary;
184 }
185
186 TEST_F(SdchFilterTest, EmptyInputOk) {
187 std::vector<Filter::FilterType> filter_types;
188 filter_types.push_back(Filter::FILTER_TYPE_SDCH);
189 char output_buffer[20];
190 std::string url_string("http://ignore.com");
191 filter_context()->SetURL(GURL(url_string));
192 std::unique_ptr<Filter> filter(
193 Filter::Factory(filter_types, *filter_context()));
194
195 // With no input data, try to read output.
196 int output_bytes_or_buffer_size = sizeof(output_buffer);
197 Filter::FilterStatus status = filter->ReadData(output_buffer,
198 &output_bytes_or_buffer_size);
199
200 EXPECT_EQ(0, output_bytes_or_buffer_size);
201 EXPECT_EQ(Filter::FILTER_NEED_MORE_DATA, status);
202 }
203
204 // Make sure that the filter context has everything that might be
205 // nuked from it during URLRequest teardown before the SdchFilter
206 // destructor.
207 TEST_F(SdchFilterTest, SparseContextOk) {
208 std::vector<Filter::FilterType> filter_types;
209 filter_types.push_back(Filter::FILTER_TYPE_SDCH);
210 char output_buffer[20];
211 std::string url_string("http://ignore.com");
212 filter_context()->SetURL(GURL(url_string));
213 std::unique_ptr<Filter> filter(
214 Filter::Factory(filter_types, *filter_context()));
215
216 // With no input data, try to read output.
217 int output_bytes_or_buffer_size = sizeof(output_buffer);
218 Filter::FilterStatus status = filter->ReadData(output_buffer,
219 &output_bytes_or_buffer_size);
220
221 EXPECT_EQ(0, output_bytes_or_buffer_size);
222 EXPECT_EQ(Filter::FILTER_NEED_MORE_DATA, status);
223
224 // Partially tear down context. Anything that goes through request()
225 // without checking it for null in the URLRequestJob::HttpFilterContext
226 // implementation is suspect. Everything that does check it for null should
227 // return null. This is to test for incorrectly relying on filter_context()
228 // from the SdchFilter destructor.
229 filter_context()->NukeUnstableInterfaces();
230 }
231
232 TEST_F(SdchFilterTest, PassThroughWhenTentative) {
233 std::vector<Filter::FilterType> filter_types;
234 // Selective a tentative filter (which can fall back to pass through).
235 filter_types.push_back(Filter::FILTER_TYPE_GZIP_HELPING_SDCH);
236 char output_buffer[20];
237 // Response code needs to be 200 to allow a pass through.
238 filter_context()->SetResponseCode(200);
239 std::string url_string("http://ignore.com");
240 filter_context()->SetURL(GURL(url_string));
241 std::unique_ptr<Filter> filter(
242 Filter::Factory(filter_types, *filter_context()));
243
244 // Supply enough data to force a pass-through mode..
245 std::string non_gzip_content("not GZIPed data");
246
247 char* input_buffer = filter->stream_buffer()->data();
248 int input_buffer_size = filter->stream_buffer_size();
249
250 EXPECT_LT(static_cast<int>(non_gzip_content.size()),
251 input_buffer_size);
252 memcpy(input_buffer, non_gzip_content.data(),
253 non_gzip_content.size());
254 filter->FlushStreamBuffer(non_gzip_content.size());
255
256 // Try to read output.
257 int output_bytes_or_buffer_size = sizeof(output_buffer);
258 Filter::FilterStatus status = filter->ReadData(output_buffer,
259 &output_bytes_or_buffer_size);
260
261 EXPECT_EQ(non_gzip_content.size(),
262 static_cast<size_t>(output_bytes_or_buffer_size));
263 ASSERT_GT(sizeof(output_buffer),
264 static_cast<size_t>(output_bytes_or_buffer_size));
265 output_buffer[output_bytes_or_buffer_size] = '\0';
266 EXPECT_TRUE(non_gzip_content == output_buffer);
267 EXPECT_EQ(Filter::FILTER_NEED_MORE_DATA, status);
268 }
269
270 TEST_F(SdchFilterTest, RefreshBadReturnCode) {
271 std::vector<Filter::FilterType> filter_types;
272 // Selective a tentative filter (which can fall back to pass through).
273 filter_types.push_back(Filter::FILTER_TYPE_SDCH_POSSIBLE);
274 char output_buffer[20];
275 // Response code needs to be 200 to allow a pass through.
276 filter_context()->SetResponseCode(403);
277 // Meta refresh will only appear for html content
278 filter_context()->SetMimeType("text/html");
279 std::string url_string("http://ignore.com");
280 filter_context()->SetURL(GURL(url_string));
281 std::unique_ptr<Filter> filter(
282 Filter::Factory(filter_types, *filter_context()));
283
284 // Supply enough data to force a pass-through mode, which means we have
285 // provided more than 9 characters that can't be a dictionary hash.
286 std::string non_sdch_content("This is not SDCH");
287
288 char* input_buffer = filter->stream_buffer()->data();
289 int input_buffer_size = filter->stream_buffer_size();
290
291 EXPECT_LT(static_cast<int>(non_sdch_content.size()),
292 input_buffer_size);
293 memcpy(input_buffer, non_sdch_content.data(),
294 non_sdch_content.size());
295 filter->FlushStreamBuffer(non_sdch_content.size());
296
297 // Try to read output.
298 int output_bytes_or_buffer_size = sizeof(output_buffer);
299 Filter::FilterStatus status = filter->ReadData(output_buffer,
300 &output_bytes_or_buffer_size);
301
302 // We should have read a long and complicated meta-refresh request.
303 EXPECT_TRUE(sizeof(output_buffer) == output_bytes_or_buffer_size);
304 // Check at least the prefix of the return.
305 EXPECT_EQ(0, strncmp(output_buffer,
306 "<head><META HTTP-EQUIV=\"Refresh\" CONTENT=\"0\"></head>",
307 sizeof(output_buffer)));
308 EXPECT_EQ(Filter::FILTER_OK, status);
309 }
310
311 TEST_F(SdchFilterTest, ErrorOnBadReturnCode) {
312 std::vector<Filter::FilterType> filter_types;
313 // Selective a tentative filter (which can fall back to pass through).
314 filter_types.push_back(Filter::FILTER_TYPE_SDCH_POSSIBLE);
315 char output_buffer[20];
316 // Response code needs to be 200 to allow a pass through.
317 filter_context()->SetResponseCode(403);
318 // Meta refresh will only appear for html content, so set to something else
319 // to induce an error (we can't meta refresh).
320 filter_context()->SetMimeType("anything");
321 std::string url_string("http://ignore.com");
322 filter_context()->SetURL(GURL(url_string));
323 std::unique_ptr<Filter> filter(
324 Filter::Factory(filter_types, *filter_context()));
325
326 // Supply enough data to force a pass-through mode, which means we have
327 // provided more than 9 characters that can't be a dictionary hash.
328 std::string non_sdch_content("This is not SDCH");
329
330 char* input_buffer = filter->stream_buffer()->data();
331 int input_buffer_size = filter->stream_buffer_size();
332
333 EXPECT_LT(static_cast<int>(non_sdch_content.size()),
334 input_buffer_size);
335 memcpy(input_buffer, non_sdch_content.data(),
336 non_sdch_content.size());
337 filter->FlushStreamBuffer(non_sdch_content.size());
338
339 // Try to read output.
340 int output_bytes_or_buffer_size = sizeof(output_buffer);
341 Filter::FilterStatus status = filter->ReadData(output_buffer,
342 &output_bytes_or_buffer_size);
343
344 EXPECT_EQ(0, output_bytes_or_buffer_size);
345 EXPECT_EQ(Filter::FILTER_ERROR, status);
346 }
347
348 TEST_F(SdchFilterTest, ErrorOnBadReturnCodeWithHtml) {
349 std::vector<Filter::FilterType> filter_types;
350 // Selective a tentative filter (which can fall back to pass through).
351 filter_types.push_back(Filter::FILTER_TYPE_SDCH_POSSIBLE);
352 char output_buffer[20];
353 // Response code needs to be 200 to allow a pass through.
354 filter_context()->SetResponseCode(403);
355 // Meta refresh will only appear for html content
356 filter_context()->SetMimeType("text/html");
357 std::string url_string("http://ignore.com");
358 filter_context()->SetURL(GURL(url_string));
359 std::unique_ptr<Filter> filter(
360 Filter::Factory(filter_types, *filter_context()));
361
362 // Supply enough data to force a pass-through mode, which means we have
363 // provided more than 9 characters that can't be a dictionary hash.
364 std::string non_sdch_content("This is not SDCH");
365
366 char* input_buffer = filter->stream_buffer()->data();
367 int input_buffer_size = filter->stream_buffer_size();
368
369 EXPECT_LT(static_cast<int>(non_sdch_content.size()),
370 input_buffer_size);
371 memcpy(input_buffer, non_sdch_content.data(),
372 non_sdch_content.size());
373 filter->FlushStreamBuffer(non_sdch_content.size());
374
375 // Try to read output.
376 int output_bytes_or_buffer_size = sizeof(output_buffer);
377 Filter::FilterStatus status = filter->ReadData(output_buffer,
378 &output_bytes_or_buffer_size);
379
380 // We should have read a long and complicated meta-refresh request.
381 EXPECT_EQ(sizeof(output_buffer),
382 static_cast<size_t>(output_bytes_or_buffer_size));
383 // Check at least the prefix of the return.
384 EXPECT_EQ(0, strncmp(output_buffer,
385 "<head><META HTTP-EQUIV=\"Refresh\" CONTENT=\"0\"></head>",
386 sizeof(output_buffer)));
387 EXPECT_EQ(Filter::FILTER_OK, status);
388 }
389
390 TEST_F(SdchFilterTest, BasicBadDictionary) {
391 std::vector<Filter::FilterType> filter_types;
392 filter_types.push_back(Filter::FILTER_TYPE_SDCH);
393 char output_buffer[20];
394 std::string url_string("http://ignore.com");
395 filter_context()->SetURL(GURL(url_string));
396 std::unique_ptr<Filter> filter(
397 Filter::Factory(filter_types, *filter_context()));
398
399 // Supply bogus data (which doesn't yet specify a full dictionary hash).
400 // Dictionary hash is 8 characters followed by a null.
401 std::string dictionary_hash_prefix("123");
402
403 char* input_buffer = filter->stream_buffer()->data();
404 int input_buffer_size = filter->stream_buffer_size();
405
406 EXPECT_LT(static_cast<int>(dictionary_hash_prefix.size()),
407 input_buffer_size);
408 memcpy(input_buffer, dictionary_hash_prefix.data(),
409 dictionary_hash_prefix.size());
410 filter->FlushStreamBuffer(dictionary_hash_prefix.size());
411
412 // With less than a dictionary specifier, try to read output.
413 int output_bytes_or_buffer_size = sizeof(output_buffer);
414 Filter::FilterStatus status = filter->ReadData(output_buffer,
415 &output_bytes_or_buffer_size);
416
417 EXPECT_EQ(0, output_bytes_or_buffer_size);
418 EXPECT_EQ(Filter::FILTER_NEED_MORE_DATA, status);
419
420 // Provide enough data to complete *a* hash, but it is bogus, and not in our
421 // list of dictionaries, so the filter should error out immediately.
422 std::string dictionary_hash_postfix("4abcd\0", 6);
423
424 CHECK_LT(dictionary_hash_postfix.size(),
425 static_cast<size_t>(input_buffer_size));
426 memcpy(input_buffer, dictionary_hash_postfix.data(),
427 dictionary_hash_postfix.size());
428 filter->FlushStreamBuffer(dictionary_hash_postfix.size());
429
430 // With a non-existant dictionary specifier, try to read output.
431 output_bytes_or_buffer_size = sizeof(output_buffer);
432 status = filter->ReadData(output_buffer, &output_bytes_or_buffer_size);
433
434 EXPECT_EQ(0, output_bytes_or_buffer_size);
435 EXPECT_EQ(Filter::FILTER_ERROR, status);
436
437 EXPECT_EQ(SDCH_DOMAIN_BLACKLIST_INCLUDES_TARGET,
438 sdch_manager_->IsInSupportedDomain(GURL(url_string)));
439 sdch_manager_->ClearBlacklistings();
440 EXPECT_EQ(SDCH_OK, sdch_manager_->IsInSupportedDomain(GURL(url_string)));
441 }
442
443 TEST_F(SdchFilterTest, DictionaryAddOnce) {
444 // Construct a valid SDCH dictionary from a VCDIFF dictionary.
445 const std::string kSampleDomain = "sdchtest.com";
446 std::string dictionary(NewSdchDictionary(kSampleDomain));
447
448 std::string url_string = "http://" + kSampleDomain;
449 GURL url(url_string);
450 EXPECT_TRUE(AddSdchDictionary(dictionary, url));
451
452 // Check we can't add it twice.
453 EXPECT_FALSE(AddSdchDictionary(dictionary, url));
454
455 const std::string kSampleDomain2 = "sdchtest2.com";
456
457 // Construct a second SDCH dictionary from a VCDIFF dictionary.
458 std::string dictionary2(NewSdchDictionary(kSampleDomain2));
459
460 std::string url_string2 = "http://" + kSampleDomain2;
461 GURL url2(url_string2);
462 EXPECT_TRUE(AddSdchDictionary(dictionary2, url2));
463 }
464
465 TEST_F(SdchFilterTest, BasicDictionary) {
466 // Construct a valid SDCH dictionary from a VCDIFF dictionary.
467 const std::string kSampleDomain = "sdchtest.com";
468 std::string dictionary(NewSdchDictionary(kSampleDomain));
469
470 std::string url_string = "http://" + kSampleDomain;
471
472 GURL url(url_string);
473 EXPECT_TRUE(AddSdchDictionary(dictionary, url));
474
475 std::string compressed(NewSdchCompressedData(dictionary));
476
477 std::vector<Filter::FilterType> filter_types;
478 filter_types.push_back(Filter::FILTER_TYPE_SDCH);
479
480 SetupFilterContextWithGURL(url);
481
482 std::unique_ptr<Filter> filter(
483 Filter::Factory(filter_types, *filter_context()));
484
485 size_t feed_block_size = 100;
486 size_t output_block_size = 100;
487 std::string output;
488 EXPECT_TRUE(FilterTestData(compressed, feed_block_size, output_block_size,
489 filter.get(), &output));
490 EXPECT_EQ(output, expanded_);
491
492 // Decode with really small buffers (size 1) to check for edge effects.
493 filter = Filter::Factory(filter_types, *filter_context());
494
495 feed_block_size = 1;
496 output_block_size = 1;
497 output.clear();
498 EXPECT_TRUE(FilterTestData(compressed, feed_block_size, output_block_size,
499 filter.get(), &output));
500 EXPECT_EQ(output, expanded_);
501 }
502
503 TEST_F(SdchFilterTest, NoDecodeHttps) {
504 // Construct a valid SDCH dictionary from a VCDIFF dictionary.
505 const std::string kSampleDomain = "sdchtest.com";
506 std::string dictionary(NewSdchDictionary(kSampleDomain));
507
508 std::string url_string = "http://" + kSampleDomain;
509
510 GURL url(url_string);
511 EXPECT_TRUE(AddSdchDictionary(dictionary, url));
512
513 std::string compressed(NewSdchCompressedData(dictionary));
514
515 std::vector<Filter::FilterType> filter_types;
516 filter_types.push_back(Filter::FILTER_TYPE_SDCH);
517
518 GURL filter_context_gurl("https://" + kSampleDomain);
519 SetupFilterContextWithGURL(GURL("https://" + kSampleDomain));
520 std::unique_ptr<Filter> filter(
521 Filter::Factory(filter_types, *filter_context()));
522
523 const size_t feed_block_size(100);
524 const size_t output_block_size(100);
525 std::string output;
526
527 EXPECT_FALSE(FilterTestData(compressed, feed_block_size, output_block_size,
528 filter.get(), &output));
529 }
530
531 // Current failsafe TODO/hack refuses to decode any content that doesn't use
532 // http as the scheme (see use of DICTIONARY_SELECTED_FOR_NON_HTTP).
533 // The following tests this blockage. Note that blacklisting results, so we
534 // we need separate tests for each of these.
535 TEST_F(SdchFilterTest, NoDecodeFtp) {
536 // Construct a valid SDCH dictionary from a VCDIFF dictionary.
537 const std::string kSampleDomain = "sdchtest.com";
538 std::string dictionary(NewSdchDictionary(kSampleDomain));
539
540 std::string url_string = "http://" + kSampleDomain;
541
542 GURL url(url_string);
543 EXPECT_TRUE(AddSdchDictionary(dictionary, url));
544
545 std::string compressed(NewSdchCompressedData(dictionary));
546
547 std::vector<Filter::FilterType> filter_types;
548 filter_types.push_back(Filter::FILTER_TYPE_SDCH);
549
550 SetupFilterContextWithGURL(GURL("ftp://" + kSampleDomain));
551 std::unique_ptr<Filter> filter(
552 Filter::Factory(filter_types, *filter_context()));
553
554 const size_t feed_block_size(100);
555 const size_t output_block_size(100);
556 std::string output;
557
558 EXPECT_FALSE(FilterTestData(compressed, feed_block_size, output_block_size,
559 filter.get(), &output));
560 }
561
562 TEST_F(SdchFilterTest, NoDecodeFileColon) {
563 // Construct a valid SDCH dictionary from a VCDIFF dictionary.
564 const std::string kSampleDomain = "sdchtest.com";
565 std::string dictionary(NewSdchDictionary(kSampleDomain));
566
567 std::string url_string = "http://" + kSampleDomain;
568
569 GURL url(url_string);
570 EXPECT_TRUE(AddSdchDictionary(dictionary, url));
571
572 std::string compressed(NewSdchCompressedData(dictionary));
573
574 std::vector<Filter::FilterType> filter_types;
575 filter_types.push_back(Filter::FILTER_TYPE_SDCH);
576
577 SetupFilterContextWithGURL(GURL("file://" + kSampleDomain));
578 std::unique_ptr<Filter> filter(
579 Filter::Factory(filter_types, *filter_context()));
580
581 const size_t feed_block_size(100);
582 const size_t output_block_size(100);
583 std::string output;
584
585 EXPECT_FALSE(FilterTestData(compressed, feed_block_size, output_block_size,
586 filter.get(), &output));
587 }
588
589 TEST_F(SdchFilterTest, NoDecodeAboutColon) {
590 // Construct a valid SDCH dictionary from a VCDIFF dictionary.
591 const std::string kSampleDomain = "sdchtest.com";
592 std::string dictionary(NewSdchDictionary(kSampleDomain));
593
594 std::string url_string = "http://" + kSampleDomain;
595
596 GURL url(url_string);
597 EXPECT_TRUE(AddSdchDictionary(dictionary, url));
598
599 std::string compressed(NewSdchCompressedData(dictionary));
600
601 std::vector<Filter::FilterType> filter_types;
602 filter_types.push_back(Filter::FILTER_TYPE_SDCH);
603
604 SetupFilterContextWithGURL(GURL("about://" + kSampleDomain));
605 std::unique_ptr<Filter> filter(
606 Filter::Factory(filter_types, *filter_context()));
607
608 const size_t feed_block_size(100);
609 const size_t output_block_size(100);
610 std::string output;
611
612 EXPECT_FALSE(FilterTestData(compressed, feed_block_size, output_block_size,
613 filter.get(), &output));
614 }
615
616 TEST_F(SdchFilterTest, NoDecodeJavaScript) {
617 // Construct a valid SDCH dictionary from a VCDIFF dictionary.
618 const std::string kSampleDomain = "sdchtest.com";
619 std::string dictionary(NewSdchDictionary(kSampleDomain));
620
621 std::string url_string = "http://" + kSampleDomain;
622
623 GURL url(url_string);
624 EXPECT_TRUE(AddSdchDictionary(dictionary, url));
625
626 std::string compressed(NewSdchCompressedData(dictionary));
627
628 std::vector<Filter::FilterType> filter_types;
629 filter_types.push_back(Filter::FILTER_TYPE_SDCH);
630
631 SetupFilterContextWithGURL(GURL("javascript://" + kSampleDomain));
632 std::unique_ptr<Filter> filter(
633 Filter::Factory(filter_types, *filter_context()));
634
635 const size_t feed_block_size(100);
636 const size_t output_block_size(100);
637 std::string output;
638
639 EXPECT_FALSE(FilterTestData(compressed, feed_block_size, output_block_size,
640 filter.get(), &output));
641 }
642
643 TEST_F(SdchFilterTest, CanStillDecodeHttp) {
644 // Construct a valid SDCH dictionary from a VCDIFF dictionary.
645 const std::string kSampleDomain = "sdchtest.com";
646 std::string dictionary(NewSdchDictionary(kSampleDomain));
647
648 std::string url_string = "http://" + kSampleDomain;
649
650 GURL url(url_string);
651 EXPECT_TRUE(AddSdchDictionary(dictionary, url));
652
653 std::string compressed(NewSdchCompressedData(dictionary));
654
655 std::vector<Filter::FilterType> filter_types;
656 filter_types.push_back(Filter::FILTER_TYPE_SDCH);
657
658 SetupFilterContextWithGURL(GURL("http://" + kSampleDomain));
659 std::unique_ptr<Filter> filter(
660 Filter::Factory(filter_types, *filter_context()));
661
662 const size_t feed_block_size(100);
663 const size_t output_block_size(100);
664 std::string output;
665
666 base::HistogramTester tester;
667
668 EXPECT_TRUE(FilterTestData(compressed, feed_block_size, output_block_size,
669 filter.get(), &output));
670 // The filter's destructor is responsible for uploading total ratio
671 // histograms.
672 filter.reset();
673
674 tester.ExpectTotalCount("Sdch3.Network_Decode_Ratio_a", 1);
675 tester.ExpectTotalCount("Sdch3.NetworkBytesSavedByCompression", 1);
676 }
677
678 TEST_F(SdchFilterTest, CrossDomainDictionaryUse) {
679 // Construct a valid SDCH dictionary from a VCDIFF dictionary.
680 const std::string kSampleDomain = "sdchtest.com";
681 std::string dictionary(NewSdchDictionary(kSampleDomain));
682
683 std::string url_string = "http://" + kSampleDomain;
684
685 GURL url(url_string);
686 EXPECT_TRUE(AddSdchDictionary(dictionary, url));
687
688 std::string compressed(NewSdchCompressedData(dictionary));
689
690 std::vector<Filter::FilterType> filter_types;
691 filter_types.push_back(Filter::FILTER_TYPE_SDCH);
692
693 // Decode with content arriving from the "wrong" domain.
694 // This tests SdchManager::CanSet().
695 GURL wrong_domain_url("http://www.wrongdomain.com");
696 SetupFilterContextWithGURL(wrong_domain_url);
697 std::unique_ptr<Filter> filter(
698 Filter::Factory(filter_types, *filter_context()));
699
700 size_t feed_block_size = 100;
701 size_t output_block_size = 100;
702 std::string output;
703 EXPECT_FALSE(FilterTestData(compressed, feed_block_size, output_block_size,
704 filter.get(), &output));
705 EXPECT_EQ(output.size(), 0u); // No output written.
706
707 EXPECT_EQ(SDCH_OK, sdch_manager_->IsInSupportedDomain(GURL(url_string)));
708 EXPECT_EQ(SDCH_DOMAIN_BLACKLIST_INCLUDES_TARGET,
709 sdch_manager_->IsInSupportedDomain(wrong_domain_url));
710 sdch_manager_->ClearBlacklistings();
711 EXPECT_EQ(SDCH_OK, sdch_manager_->IsInSupportedDomain(wrong_domain_url));
712 }
713
714 TEST_F(SdchFilterTest, DictionaryPathValidation) {
715 // Construct a valid SDCH dictionary from a VCDIFF dictionary.
716 const std::string kSampleDomain = "sdchtest.com";
717 std::string dictionary(NewSdchDictionary(kSampleDomain));
718
719 std::string url_string = "http://" + kSampleDomain;
720
721 GURL url(url_string);
722 EXPECT_TRUE(AddSdchDictionary(dictionary, url));
723
724 // Create a dictionary with a path restriction, by prefixing dictionary.
725 const std::string path("/special_path/bin");
726 std::string dictionary_with_path("Path: " + path + "\n");
727 dictionary_with_path.append(dictionary);
728 GURL url2(url_string + path);
729 EXPECT_TRUE(AddSdchDictionary(dictionary_with_path, url2));
730
731 std::string compressed_for_path(NewSdchCompressedData(dictionary_with_path));
732
733 std::vector<Filter::FilterType> filter_types;
734 filter_types.push_back(Filter::FILTER_TYPE_SDCH);
735
736 // Test decode the path data, arriving from a valid path.
737 SetupFilterContextWithGURL(GURL(url_string + path));
738 std::unique_ptr<Filter> filter(
739 Filter::Factory(filter_types, *filter_context()));
740
741 size_t feed_block_size = 100;
742 size_t output_block_size = 100;
743 std::string output;
744
745 EXPECT_TRUE(FilterTestData(compressed_for_path, feed_block_size,
746 output_block_size, filter.get(), &output));
747 EXPECT_EQ(output, expanded_);
748
749 // Test decode the path data, arriving from a invalid path.
750 SetupFilterContextWithGURL(GURL(url_string));
751 filter = Filter::Factory(filter_types, *filter_context());
752
753 feed_block_size = 100;
754 output_block_size = 100;
755 output.clear();
756 EXPECT_FALSE(FilterTestData(compressed_for_path, feed_block_size,
757 output_block_size, filter.get(), &output));
758 EXPECT_EQ(output.size(), 0u); // No output written.
759
760 EXPECT_EQ(SDCH_DOMAIN_BLACKLIST_INCLUDES_TARGET,
761 sdch_manager_->IsInSupportedDomain(GURL(url_string)));
762 sdch_manager_->ClearBlacklistings();
763 EXPECT_EQ(SDCH_OK, sdch_manager_->IsInSupportedDomain(GURL(url_string)));
764 }
765
766 TEST_F(SdchFilterTest, DictionaryPortValidation) {
767 // Construct a valid SDCH dictionary from a VCDIFF dictionary.
768 const std::string kSampleDomain = "sdchtest.com";
769 std::string dictionary(NewSdchDictionary(kSampleDomain));
770
771 std::string url_string = "http://" + kSampleDomain;
772
773 GURL url(url_string);
774 EXPECT_TRUE(AddSdchDictionary(dictionary, url));
775
776 // Create a dictionary with a port restriction, by prefixing old dictionary.
777 const std::string port("502");
778 std::string dictionary_with_port("Port: " + port + "\n");
779 dictionary_with_port.append("Port: 80\n"); // Add default port.
780 dictionary_with_port.append(dictionary);
781 EXPECT_TRUE(AddSdchDictionary(dictionary_with_port,
782 GURL(url_string + ":" + port)));
783
784 std::string compressed_for_port(NewSdchCompressedData(dictionary_with_port));
785
786 std::vector<Filter::FilterType> filter_types;
787 filter_types.push_back(Filter::FILTER_TYPE_SDCH);
788
789 // Test decode the port data, arriving from a valid port.
790 SetupFilterContextWithGURL(GURL(url_string + ":" + port));
791 std::unique_ptr<Filter> filter(
792 Filter::Factory(filter_types, *filter_context()));
793
794 size_t feed_block_size = 100;
795 size_t output_block_size = 100;
796 std::string output;
797 EXPECT_TRUE(FilterTestData(compressed_for_port, feed_block_size,
798 output_block_size, filter.get(), &output));
799 EXPECT_EQ(output, expanded_);
800
801 // Test decode the port data, arriving from a valid (default) port.
802 SetupFilterContextWithGURL(GURL(url_string)); // Default port.
803 filter = Filter::Factory(filter_types, *filter_context());
804
805 feed_block_size = 100;
806 output_block_size = 100;
807 output.clear();
808 EXPECT_TRUE(FilterTestData(compressed_for_port, feed_block_size,
809 output_block_size, filter.get(), &output));
810 EXPECT_EQ(output, expanded_);
811
812 // Test decode the port data, arriving from a invalid port.
813 SetupFilterContextWithGURL(GURL(url_string + ":" + port + "1"));
814 filter = Filter::Factory(filter_types, *filter_context());
815
816 feed_block_size = 100;
817 output_block_size = 100;
818 output.clear();
819 EXPECT_FALSE(FilterTestData(compressed_for_port, feed_block_size,
820 output_block_size, filter.get(), &output));
821 EXPECT_EQ(output.size(), 0u); // No output written.
822
823 EXPECT_EQ(SDCH_DOMAIN_BLACKLIST_INCLUDES_TARGET,
824 sdch_manager_->IsInSupportedDomain(GURL(url_string)));
825 sdch_manager_->ClearBlacklistings();
826 EXPECT_EQ(SDCH_OK, sdch_manager_->IsInSupportedDomain(GURL(url_string)));
827 }
828
829 // Helper function to perform gzip compression of data.
830 static std::string gzip_compress(const std::string &input) {
831 z_stream zlib_stream;
832 memset(&zlib_stream, 0, sizeof(zlib_stream));
833 int code;
834
835 // Initialize zlib
836 code = deflateInit2(&zlib_stream, Z_DEFAULT_COMPRESSION, Z_DEFLATED,
837 -MAX_WBITS,
838 8, // DEF_MEM_LEVEL
839 Z_DEFAULT_STRATEGY);
840
841 CHECK_EQ(Z_OK, code);
842
843 // Fill in zlib control block
844 zlib_stream.next_in = bit_cast<Bytef*>(input.data());
845 zlib_stream.avail_in = input.size();
846
847 // Assume we can compress into similar buffer (add 100 bytes to be sure).
848 size_t gzip_compressed_length = zlib_stream.avail_in + 100;
849 std::unique_ptr<char[]> gzip_compressed(new char[gzip_compressed_length]);
850 zlib_stream.next_out = bit_cast<Bytef*>(gzip_compressed.get());
851 zlib_stream.avail_out = gzip_compressed_length;
852
853 // The GZIP header (see RFC 1952):
854 // +---+---+---+---+---+---+---+---+---+---+
855 // |ID1|ID2|CM |FLG| MTIME |XFL|OS |
856 // +---+---+---+---+---+---+---+---+---+---+
857 // ID1 \037
858 // ID2 \213
859 // CM \010 (compression method == DEFLATE)
860 // FLG \000 (special flags that we do not support)
861 // MTIME Unix format modification time (0 means not available)
862 // XFL 2-4? DEFLATE flags
863 // OS ???? Operating system indicator (255 means unknown)
864 //
865 // Header value we generate:
866 const char kGZipHeader[] = { '\037', '\213', '\010', '\000', '\000',
867 '\000', '\000', '\000', '\002', '\377' };
868 CHECK_GT(zlib_stream.avail_out, sizeof(kGZipHeader));
869 memcpy(zlib_stream.next_out, kGZipHeader, sizeof(kGZipHeader));
870 zlib_stream.next_out += sizeof(kGZipHeader);
871 zlib_stream.avail_out -= sizeof(kGZipHeader);
872
873 // Do deflate
874 code = deflate(&zlib_stream, Z_FINISH);
875 gzip_compressed_length -= zlib_stream.avail_out;
876 std::string compressed(gzip_compressed.get(), gzip_compressed_length);
877 deflateEnd(&zlib_stream);
878 return compressed;
879 }
880
881 //------------------------------------------------------------------------------
882
883 class SdchFilterChainingTest {
884 public:
885 static std::unique_ptr<Filter> Factory(
886 const std::vector<Filter::FilterType>& types,
887 const FilterContext& context,
888 int size) {
889 return Filter::FactoryForTests(types, context, size);
890 }
891 };
892
893 // Test that filters can be cascaded (chained) so that the output of one filter
894 // is processed by the next one. This is most critical for SDCH, which is
895 // routinely followed by gzip (during encoding). The filter we'll test for will
896 // do the gzip decoding first, and then decode the SDCH content.
897 TEST_F(SdchFilterTest, FilterChaining) {
898 // Construct a valid SDCH dictionary from a VCDIFF dictionary.
899 const std::string kSampleDomain = "sdchtest.com";
900 std::string dictionary(NewSdchDictionary(kSampleDomain));
901
902 std::string url_string = "http://" + kSampleDomain;
903
904 GURL url(url_string);
905 EXPECT_TRUE(AddSdchDictionary(dictionary, url));
906
907 std::string sdch_compressed(NewSdchCompressedData(dictionary));
908
909 // Use Gzip to compress the sdch sdch_compressed data.
910 std::string gzip_compressed_sdch = gzip_compress(sdch_compressed);
911
912 // Construct a chained filter.
913 std::vector<Filter::FilterType> filter_types;
914 filter_types.push_back(Filter::FILTER_TYPE_SDCH);
915 filter_types.push_back(Filter::FILTER_TYPE_GZIP);
916
917 // First try with a large buffer (larger than test input, or compressed data).
918 const size_t kLargeInputBufferSize(1000); // Used internally in filters.
919 CHECK_GT(kLargeInputBufferSize, gzip_compressed_sdch.size());
920 CHECK_GT(kLargeInputBufferSize, sdch_compressed.size());
921 CHECK_GT(kLargeInputBufferSize, expanded_.size());
922 SetupFilterContextWithGURL(url);
923 std::unique_ptr<Filter> filter(SdchFilterChainingTest::Factory(
924 filter_types, *filter_context(), kLargeInputBufferSize));
925 EXPECT_EQ(static_cast<int>(kLargeInputBufferSize),
926 filter->stream_buffer_size());
927
928 // Verify that chained filter is waiting for data.
929 char tiny_output_buffer[10];
930 int tiny_output_size = sizeof(tiny_output_buffer);
931 EXPECT_EQ(Filter::FILTER_NEED_MORE_DATA,
932 filter->ReadData(tiny_output_buffer, &tiny_output_size));
933
934 // Make chain process all data.
935 size_t feed_block_size = kLargeInputBufferSize;
936 size_t output_block_size = kLargeInputBufferSize;
937 std::string output;
938 EXPECT_TRUE(FilterTestData(gzip_compressed_sdch, feed_block_size,
939 output_block_size, filter.get(), &output));
940 EXPECT_EQ(output, expanded_);
941
942 // Next try with a mid-sized internal buffer size.
943 const size_t kMidSizedInputBufferSize(100);
944 // Buffer should be big enough to swallow whole gzip content.
945 CHECK_GT(kMidSizedInputBufferSize, gzip_compressed_sdch.size());
946 // Buffer should be small enough that entire SDCH content can't fit.
947 // We'll go even further, and force the chain to flush the buffer between the
948 // two filters more than once (that is why we multiply by 2).
949 CHECK_LT(kMidSizedInputBufferSize * 2, sdch_compressed.size());
950 filter_context()->SetURL(url);
951 filter = SdchFilterChainingTest::Factory(filter_types, *filter_context(),
952 kMidSizedInputBufferSize);
953 EXPECT_EQ(static_cast<int>(kMidSizedInputBufferSize),
954 filter->stream_buffer_size());
955
956 feed_block_size = kMidSizedInputBufferSize;
957 output_block_size = kMidSizedInputBufferSize;
958 output.clear();
959 EXPECT_TRUE(FilterTestData(gzip_compressed_sdch, feed_block_size,
960 output_block_size, filter.get(), &output));
961 EXPECT_EQ(output, expanded_);
962
963 // Next try with a tiny input and output buffer to cover edge effects.
964 filter = SdchFilterChainingTest::Factory(filter_types, *filter_context(),
965 kLargeInputBufferSize);
966 EXPECT_EQ(static_cast<int>(kLargeInputBufferSize),
967 filter->stream_buffer_size());
968
969 feed_block_size = 1;
970 output_block_size = 1;
971 output.clear();
972 EXPECT_TRUE(FilterTestData(gzip_compressed_sdch, feed_block_size,
973 output_block_size, filter.get(), &output));
974 EXPECT_EQ(output, expanded_);
975 }
976
977 // Test that filters can be cascaded (chained) so that the output of one filter
978 // is processed by the next one. This is most critical for SDCH, which is
979 // routinely followed by gzip (during encoding). The filter we'll test for will
980 // do the gzip decoding first, and then decode the SDCH content and start
981 // doing gzip decoding again, which should result in FILTER_ERROR and
982 // empty output buffer.
983 TEST_F(SdchFilterTest, FilterDoubleChaining) {
984 // Construct a valid SDCH dictionary from a VCDIFF dictionary.
985 const std::string kSampleDomain = "sdchtest.com";
986 std::string dictionary(NewSdchDictionary(kSampleDomain));
987
988 std::string url_string = "http://" + kSampleDomain;
989
990 GURL url(url_string);
991 EXPECT_TRUE(AddSdchDictionary(dictionary, url));
992
993 std::string sdch_compressed(NewSdchCompressedData(dictionary));
994
995 // Use Gzip to compress the sdch sdch_compressed data.
996 std::string gzip_compressed_sdch = gzip_compress(sdch_compressed);
997
998 // Construct a chained filter.
999 std::vector<Filter::FilterType> filter_types;
1000 filter_types.push_back(Filter::FILTER_TYPE_SDCH);
1001 filter_types.push_back(Filter::FILTER_TYPE_GZIP);
1002 filter_types.push_back(Filter::FILTER_TYPE_SDCH);
1003 filter_types.push_back(Filter::FILTER_TYPE_GZIP);
1004
1005 // First try with a large buffer (larger than test input, or compressed data).
1006 const size_t kLargeInputBufferSize(1000); // Used internally in filters.
1007 CHECK_GT(kLargeInputBufferSize, gzip_compressed_sdch.size());
1008 CHECK_GT(kLargeInputBufferSize, sdch_compressed.size());
1009 CHECK_GT(kLargeInputBufferSize, expanded_.size());
1010 SetupFilterContextWithGURL(url);
1011 std::unique_ptr<Filter> filter(SdchFilterChainingTest::Factory(
1012 filter_types, *filter_context(), kLargeInputBufferSize));
1013 EXPECT_EQ(static_cast<int>(kLargeInputBufferSize),
1014 filter->stream_buffer_size());
1015
1016 // Verify that chained filter is waiting for data.
1017 char tiny_output_buffer[10];
1018 int tiny_output_size = sizeof(tiny_output_buffer);
1019 EXPECT_EQ(Filter::FILTER_NEED_MORE_DATA,
1020 filter->ReadData(tiny_output_buffer, &tiny_output_size));
1021
1022 // Make chain process all data.
1023 size_t feed_block_size = kLargeInputBufferSize;
1024 size_t output_block_size = kLargeInputBufferSize;
1025 std::string output;
1026 EXPECT_FALSE(FilterTestData(gzip_compressed_sdch, feed_block_size,
1027 output_block_size, filter.get(), &output));
1028 EXPECT_EQ("", output);
1029
1030 // Next try with a mid-sized internal buffer size.
1031 const size_t kMidSizedInputBufferSize(100);
1032 // Buffer should be big enough to swallow whole gzip content.
1033 CHECK_GT(kMidSizedInputBufferSize, gzip_compressed_sdch.size());
1034 // Buffer should be small enough that entire SDCH content can't fit.
1035 // We'll go even further, and force the chain to flush the buffer between the
1036 // two filters more than once (that is why we multiply by 2).
1037 CHECK_LT(kMidSizedInputBufferSize * 2, sdch_compressed.size());
1038 filter_context()->SetURL(url);
1039 filter = SdchFilterChainingTest::Factory(filter_types, *filter_context(),
1040 kMidSizedInputBufferSize);
1041 EXPECT_EQ(static_cast<int>(kMidSizedInputBufferSize),
1042 filter->stream_buffer_size());
1043
1044 feed_block_size = kMidSizedInputBufferSize;
1045 output_block_size = kMidSizedInputBufferSize;
1046 output.clear();
1047 EXPECT_FALSE(FilterTestData(gzip_compressed_sdch, feed_block_size,
1048 output_block_size, filter.get(), &output));
1049 EXPECT_EQ("", output);
1050
1051 // Next try with a tiny input and output buffer to cover edge effects.
1052 filter = SdchFilterChainingTest::Factory(filter_types, *filter_context(),
1053 kLargeInputBufferSize);
1054 EXPECT_EQ(static_cast<int>(kLargeInputBufferSize),
1055 filter->stream_buffer_size());
1056
1057 feed_block_size = 1;
1058 output_block_size = 1;
1059 output.clear();
1060 EXPECT_FALSE(FilterTestData(gzip_compressed_sdch, feed_block_size,
1061 output_block_size, filter.get(), &output));
1062 EXPECT_EQ("", output);
1063 }
1064
1065 TEST_F(SdchFilterTest, DefaultGzipIfSdch) {
1066 // Construct a valid SDCH dictionary from a VCDIFF dictionary.
1067 const std::string kSampleDomain = "sdchtest.com";
1068 std::string dictionary(NewSdchDictionary(kSampleDomain));
1069
1070 std::string url_string = "http://" + kSampleDomain;
1071
1072 GURL url(url_string);
1073 EXPECT_TRUE(AddSdchDictionary(dictionary, url));
1074
1075 std::string sdch_compressed(NewSdchCompressedData(dictionary));
1076
1077 // Use Gzip to compress the sdch sdch_compressed data.
1078 std::string gzip_compressed_sdch = gzip_compress(sdch_compressed);
1079
1080 // Only claim to have sdch content, but really use the gzipped sdch content.
1081 // System should automatically add the missing (optional) gzip.
1082 std::vector<Filter::FilterType> filter_types;
1083 filter_types.push_back(Filter::FILTER_TYPE_SDCH);
1084
1085 filter_context()->SetMimeType("anything/mime");
1086 SetupFilterContextWithGURL(url);
1087
1088 Filter::FixupEncodingTypes(*filter_context(), &filter_types);
1089 ASSERT_EQ(filter_types.size(), 2u);
1090 EXPECT_EQ(filter_types[0], Filter::FILTER_TYPE_SDCH);
1091 EXPECT_EQ(filter_types[1], Filter::FILTER_TYPE_GZIP_HELPING_SDCH);
1092
1093 // First try with a large buffer (larger than test input, or compressed data).
1094 std::unique_ptr<Filter> filter(
1095 Filter::Factory(filter_types, *filter_context()));
1096
1097 // Verify that chained filter is waiting for data.
1098 char tiny_output_buffer[10];
1099 int tiny_output_size = sizeof(tiny_output_buffer);
1100 EXPECT_EQ(Filter::FILTER_NEED_MORE_DATA,
1101 filter->ReadData(tiny_output_buffer, &tiny_output_size));
1102
1103 size_t feed_block_size = 100;
1104 size_t output_block_size = 100;
1105 std::string output;
1106 EXPECT_TRUE(FilterTestData(gzip_compressed_sdch, feed_block_size,
1107 output_block_size, filter.get(), &output));
1108 EXPECT_EQ(output, expanded_);
1109
1110 // Next try with a tiny buffer to cover edge effects.
1111 filter = Filter::Factory(filter_types, *filter_context());
1112
1113 feed_block_size = 1;
1114 output_block_size = 1;
1115 output.clear();
1116 EXPECT_TRUE(FilterTestData(gzip_compressed_sdch, feed_block_size,
1117 output_block_size, filter.get(), &output));
1118 EXPECT_EQ(output, expanded_);
1119 }
1120
1121 TEST_F(SdchFilterTest, AcceptGzipSdchIfGzip) {
1122 // Construct a valid SDCH dictionary from a VCDIFF dictionary.
1123 const std::string kSampleDomain = "sdchtest.com";
1124 std::string dictionary(NewSdchDictionary(kSampleDomain));
1125
1126 std::string url_string = "http://" + kSampleDomain;
1127
1128 GURL url(url_string);
1129 EXPECT_TRUE(AddSdchDictionary(dictionary, url));
1130
1131 std::string sdch_compressed(NewSdchCompressedData(dictionary));
1132
1133 // Use Gzip to compress the sdch sdch_compressed data.
1134 std::string gzip_compressed_sdch = gzip_compress(sdch_compressed);
1135
1136 // Some proxies strip the content encoding statement down to a mere gzip, but
1137 // pass through the original content (with full sdch,gzip encoding).
1138 // Only claim to have gzip content, but really use the gzipped sdch content.
1139 // System should automatically add the missing (optional) sdch.
1140 std::vector<Filter::FilterType> filter_types;
1141 filter_types.push_back(Filter::FILTER_TYPE_GZIP);
1142
1143 filter_context()->SetMimeType("anything/mime");
1144 SetupFilterContextWithGURL(url);
1145 Filter::FixupEncodingTypes(*filter_context(), &filter_types);
1146 ASSERT_EQ(filter_types.size(), 3u);
1147 EXPECT_EQ(filter_types[0], Filter::FILTER_TYPE_SDCH_POSSIBLE);
1148 EXPECT_EQ(filter_types[1], Filter::FILTER_TYPE_GZIP_HELPING_SDCH);
1149 EXPECT_EQ(filter_types[2], Filter::FILTER_TYPE_GZIP);
1150
1151 // First try with a large buffer (larger than test input, or compressed data).
1152 std::unique_ptr<Filter> filter(
1153 Filter::Factory(filter_types, *filter_context()));
1154
1155 // Verify that chained filter is waiting for data.
1156 char tiny_output_buffer[10];
1157 int tiny_output_size = sizeof(tiny_output_buffer);
1158 EXPECT_EQ(Filter::FILTER_NEED_MORE_DATA,
1159 filter->ReadData(tiny_output_buffer, &tiny_output_size));
1160
1161 size_t feed_block_size = 100;
1162 size_t output_block_size = 100;
1163 std::string output;
1164 EXPECT_TRUE(FilterTestData(gzip_compressed_sdch, feed_block_size,
1165 output_block_size, filter.get(), &output));
1166 EXPECT_EQ(output, expanded_);
1167
1168 // Next try with a tiny buffer to cover edge effects.
1169 filter = Filter::Factory(filter_types, *filter_context());
1170
1171 feed_block_size = 1;
1172 output_block_size = 1;
1173 output.clear();
1174 EXPECT_TRUE(FilterTestData(gzip_compressed_sdch, feed_block_size,
1175 output_block_size, filter.get(), &output));
1176 EXPECT_EQ(output, expanded_);
1177 }
1178
1179 TEST_F(SdchFilterTest, DefaultSdchGzipIfEmpty) {
1180 // Construct a valid SDCH dictionary from a VCDIFF dictionary.
1181 const std::string kSampleDomain = "sdchtest.com";
1182 std::string dictionary(NewSdchDictionary(kSampleDomain));
1183
1184 std::string url_string = "http://" + kSampleDomain;
1185
1186 GURL url(url_string);
1187 EXPECT_TRUE(AddSdchDictionary(dictionary, url));
1188
1189 std::string sdch_compressed(NewSdchCompressedData(dictionary));
1190
1191 // Use Gzip to compress the sdch sdch_compressed data.
1192 std::string gzip_compressed_sdch = gzip_compress(sdch_compressed);
1193
1194 // Only claim to have non-encoded content, but really use the gzipped sdch
1195 // content.
1196 // System should automatically add the missing (optional) sdch,gzip.
1197 std::vector<Filter::FilterType> filter_types;
1198
1199 filter_context()->SetMimeType("anything/mime");
1200 SetupFilterContextWithGURL(url);
1201 Filter::FixupEncodingTypes(*filter_context(), &filter_types);
1202 ASSERT_EQ(filter_types.size(), 2u);
1203 EXPECT_EQ(filter_types[0], Filter::FILTER_TYPE_SDCH_POSSIBLE);
1204 EXPECT_EQ(filter_types[1], Filter::FILTER_TYPE_GZIP_HELPING_SDCH);
1205
1206 // First try with a large buffer (larger than test input, or compressed data).
1207 std::unique_ptr<Filter> filter(
1208 Filter::Factory(filter_types, *filter_context()));
1209
1210 // Verify that chained filter is waiting for data.
1211 char tiny_output_buffer[10];
1212 int tiny_output_size = sizeof(tiny_output_buffer);
1213 EXPECT_EQ(Filter::FILTER_NEED_MORE_DATA,
1214 filter->ReadData(tiny_output_buffer, &tiny_output_size));
1215
1216 size_t feed_block_size = 100;
1217 size_t output_block_size = 100;
1218 std::string output;
1219 EXPECT_TRUE(FilterTestData(gzip_compressed_sdch, feed_block_size,
1220 output_block_size, filter.get(), &output));
1221 EXPECT_EQ(output, expanded_);
1222
1223 // Next try with a tiny buffer to cover edge effects.
1224 filter = Filter::Factory(filter_types, *filter_context());
1225
1226 feed_block_size = 1;
1227 output_block_size = 1;
1228 output.clear();
1229 EXPECT_TRUE(FilterTestData(gzip_compressed_sdch, feed_block_size,
1230 output_block_size, filter.get(), &output));
1231 EXPECT_EQ(output, expanded_);
1232 }
1233
1234 TEST_F(SdchFilterTest, AcceptGzipGzipSdchIfGzip) {
1235 // Construct a valid SDCH dictionary from a VCDIFF dictionary.
1236 const std::string kSampleDomain = "sdchtest.com";
1237 std::string dictionary(NewSdchDictionary(kSampleDomain));
1238
1239 std::string url_string = "http://" + kSampleDomain;
1240
1241 GURL url(url_string);
1242 EXPECT_TRUE(AddSdchDictionary(dictionary, url));
1243
1244 std::string sdch_compressed(NewSdchCompressedData(dictionary));
1245
1246 // Vodaphone (UK) Mobile Broadband provides double gzipped sdch with a content
1247 // encoding of merely gzip (apparently, only listing the extra level of
1248 // wrapper compression they added, but discarding the actual content encoding.
1249 // Use Gzip to double compress the sdch sdch_compressed data.
1250 std::string double_gzip_compressed_sdch = gzip_compress(gzip_compress(
1251 sdch_compressed));
1252
1253 // Only claim to have gzip content, but really use the double gzipped sdch
1254 // content.
1255 // System should automatically add the missing (optional) sdch, gzip decoders.
1256 std::vector<Filter::FilterType> filter_types;
1257 filter_types.push_back(Filter::FILTER_TYPE_GZIP);
1258
1259 filter_context()->SetMimeType("anything/mime");
1260 SetupFilterContextWithGURL(url);
1261 Filter::FixupEncodingTypes(*filter_context(), &filter_types);
1262 ASSERT_EQ(filter_types.size(), 3u);
1263 EXPECT_EQ(filter_types[0], Filter::FILTER_TYPE_SDCH_POSSIBLE);
1264 EXPECT_EQ(filter_types[1], Filter::FILTER_TYPE_GZIP_HELPING_SDCH);
1265 EXPECT_EQ(filter_types[2], Filter::FILTER_TYPE_GZIP);
1266
1267 // First try with a large buffer (larger than test input, or compressed data).
1268 std::unique_ptr<Filter> filter(
1269 Filter::Factory(filter_types, *filter_context()));
1270
1271 // Verify that chained filter is waiting for data.
1272 char tiny_output_buffer[10];
1273 int tiny_output_size = sizeof(tiny_output_buffer);
1274 EXPECT_EQ(Filter::FILTER_NEED_MORE_DATA,
1275 filter->ReadData(tiny_output_buffer, &tiny_output_size));
1276
1277 size_t feed_block_size = 100;
1278 size_t output_block_size = 100;
1279 std::string output;
1280 EXPECT_TRUE(FilterTestData(double_gzip_compressed_sdch, feed_block_size,
1281 output_block_size, filter.get(), &output));
1282 EXPECT_EQ(output, expanded_);
1283
1284 // Next try with a tiny buffer to cover edge effects.
1285 filter = Filter::Factory(filter_types, *filter_context());
1286
1287 feed_block_size = 1;
1288 output_block_size = 1;
1289 output.clear();
1290 EXPECT_TRUE(FilterTestData(double_gzip_compressed_sdch, feed_block_size,
1291 output_block_size, filter.get(), &output));
1292 EXPECT_EQ(output, expanded_);
1293 }
1294
1295 // Test to make sure we decode properly with an unexpected dictionary.
1296 TEST_F(SdchFilterTest, UnexpectedDictionary) {
1297 // Setup a dictionary, add it to the filter context, and create a filter
1298 // based on that dictionary.
1299 const std::string kSampleDomain = "sdchtest.com";
1300 std::string dictionary(NewSdchDictionary(kSampleDomain));
1301 std::string url_string = "http://" + kSampleDomain;
1302 GURL url(url_string);
1303 EXPECT_TRUE(AddSdchDictionary(dictionary, url));
1304
1305 SetupFilterContextWithGURL(url);
1306
1307 std::vector<Filter::FilterType> filter_types;
1308 filter_types.push_back(Filter::FILTER_TYPE_SDCH);
1309 std::unique_ptr<Filter> filter(
1310 Filter::Factory(filter_types, *filter_context()));
1311
1312 // Setup another dictionary, expired. Don't add it to the filter context.
1313 // Delete stored dictionaries first to handle platforms which only
1314 // have room for a single dictionary.
1315 sdch_manager_->ClearData();
1316 std::string expired_dictionary(NewSdchExpiredDictionary(kSampleDomain));
1317
1318 // Don't use the Helper function since its insertion check is indeterminate
1319 // for a Max-Age: 0 dictionary.
1320 sdch_manager_->AddSdchDictionary(expired_dictionary, url, nullptr);
1321
1322 std::string client_hash;
1323 std::string server_hash;
1324 SdchManager::GenerateHash(expired_dictionary, &client_hash, &server_hash);
1325
1326 SdchProblemCode problem_code;
1327 std::unique_ptr<SdchManager::DictionarySet> hash_set(
1328 sdch_manager_->GetDictionarySetByHash(url, server_hash, &problem_code));
1329 ASSERT_TRUE(hash_set);
1330 ASSERT_EQ(SDCH_OK, problem_code);
1331
1332 // Encode output with the second dictionary.
1333 std::string sdch_compressed(NewSdchCompressedData(expired_dictionary));
1334
1335 // See if the filter decodes it.
1336 std::string output;
1337 EXPECT_TRUE(FilterTestData(sdch_compressed, 100, 100, filter.get(), &output));
1338 EXPECT_EQ(expanded_, output);
1339 }
1340
1341 class SimpleSdchObserver : public SdchObserver {
1342 public:
1343 explicit SimpleSdchObserver(SdchManager* manager)
1344 : dictionary_used_(0), manager_(manager) {
1345 manager_->AddObserver(this);
1346 }
1347 ~SimpleSdchObserver() override { manager_->RemoveObserver(this); }
1348
1349 // SdchObserver
1350 void OnDictionaryUsed(const std::string& server_hash) override {
1351 dictionary_used_++;
1352 last_server_hash_ = server_hash;
1353 }
1354
1355 int dictionary_used_calls() const { return dictionary_used_; }
1356 std::string last_server_hash() const { return last_server_hash_; }
1357
1358 void OnDictionaryAdded(const GURL& /* dictionary_url */,
1359 const std::string& /* server_hash */) override {}
1360 void OnDictionaryRemoved(const std::string& /* server_hash */) override {}
1361 void OnGetDictionary(const GURL& /* request_url */,
1362 const GURL& /* dictionary_url */) override {}
1363 void OnClearDictionaries() override {}
1364
1365 private:
1366 int dictionary_used_;
1367 std::string last_server_hash_;
1368 SdchManager* manager_;
1369
1370 DISALLOW_COPY_AND_ASSIGN(SimpleSdchObserver);
1371 };
1372
1373 TEST_F(SdchFilterTest, DictionaryUsedSignaled) {
1374 // Construct a valid SDCH dictionary from a VCDIFF dictionary.
1375 const std::string kSampleDomain = "sdchtest.com";
1376 std::string dictionary(NewSdchDictionary(kSampleDomain));
1377 SimpleSdchObserver observer(sdch_manager_.get());
1378
1379 std::string url_string = "http://" + kSampleDomain;
1380
1381 GURL url(url_string);
1382 EXPECT_TRUE(AddSdchDictionary(dictionary, url));
1383
1384 std::string client_hash;
1385 std::string server_hash;
1386 SdchManager::GenerateHash(dictionary, &client_hash, &server_hash);
1387
1388 std::string compressed(NewSdchCompressedData(dictionary));
1389
1390 std::vector<Filter::FilterType> filter_types;
1391 filter_types.push_back(Filter::FILTER_TYPE_SDCH);
1392
1393 SetupFilterContextWithGURL(url);
1394
1395 std::unique_ptr<Filter> filter(
1396 Filter::Factory(filter_types, *filter_context()));
1397
1398 size_t feed_block_size = 100;
1399 size_t output_block_size = 100;
1400 std::string output;
1401 EXPECT_TRUE(FilterTestData(compressed, feed_block_size, output_block_size,
1402 filter.get(), &output));
1403 EXPECT_EQ(output, expanded_);
1404
1405 filter.reset(nullptr);
1406
1407 // Confirm that we got a "DictionaryUsed" signal from the SdchManager
1408 // for our dictionary.
1409 EXPECT_EQ(1, observer.dictionary_used_calls());
1410 EXPECT_EQ(server_hash, observer.last_server_hash());
1411 }
1412
1413 } // namespace net
OLDNEW
« no previous file with comments | « net/filter/sdch_filter.cc ('k') | net/net.gypi » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698