Index: net/filter/sdch_filter_unittest.cc |
diff --git a/net/filter/sdch_filter_unittest.cc b/net/filter/sdch_filter_unittest.cc |
index 79b567369d2d7dafb4e34e10b5c81a9b706eb1c6..be9790d643d4600049dd7622fec98e5e794709e4 100644 |
--- a/net/filter/sdch_filter_unittest.cc |
+++ b/net/filter/sdch_filter_unittest.cc |
@@ -10,6 +10,7 @@ |
#include "base/logging.h" |
#include "base/memory/scoped_ptr.h" |
+#include "base/test/simple_test_clock.h" |
#include "net/base/io_buffer.h" |
#include "net/filter/mock_filter_context.h" |
#include "net/filter/sdch_filter.h" |
@@ -25,12 +26,12 @@ namespace net { |
// Note an SDCH dictionary has extra meta-data before the VCDIFF dictionary. |
static const char kTestVcdiffDictionary[] = "DictionaryFor" |
"SdchCompression1SdchCompression2SdchCompression3SdchCompression\n"; |
-// Pre-compression test data. Note that we pad with a lot of highly gzip |
-// compressible content to help to exercise the chaining pipeline. That is why |
+// Pre-compression test data. Note that we pad with a lot of highly gzip |
+// compressible content to help to exercise the chaining pipeline. That is why |
// there are a PILE of zeros at the start and end. |
// This will ensure that gzip compressed data can be fed to the chain in one |
// gulp, but (with careful selection of intermediate buffers) that it takes |
-// several sdch buffers worth of data to satisfy the sdch filter. See detailed |
+// several sdch buffers worth of data to satisfy the sdch filter. See detailed |
// CHECK() calls in FilterChaining test for specifics. |
static const char kTestData[] = "0000000000000000000000000000000000000000000000" |
"0000000000000000000000000000TestData " |
@@ -63,8 +64,8 @@ class SdchFilterTest : public testing::Test { |
url_request_context->set_sdch_manager(sdch_manager_.get()); |
} |
- // Attempt to add a dictionary to the manager; returns whether or not |
- // the attempt succeeded. |
+ // Attempt to add a dictionary to the manager and probe for success or |
+ // failure. |
bool AddSdchDictionary(const std::string& dictionary_text, |
const GURL& gurl) { |
return sdch_manager_->AddSdchDictionary(dictionary_text, gurl) == SDCH_OK; |
@@ -72,6 +73,13 @@ class SdchFilterTest : public testing::Test { |
MockFilterContext* filter_context() { return filter_context_.get(); } |
+ // Sets both the GURL and the SDCH response for a filter context. |
+ void SetupFilterContextWithGURL(GURL url) { |
+ filter_context_->SetURL(url); |
+ filter_context_->SetSdchResponse( |
+ sdch_manager_->GetDictionarySet(url).Pass()); |
+ } |
+ |
std::string NewSdchCompressedData(const std::string dictionary) { |
std::string client_hash; |
std::string server_hash; |
@@ -108,11 +116,11 @@ TEST_F(SdchFilterTest, Hashing) { |
//------------------------------------------------------------------------------ |
// Provide a generic helper function for trying to filter data. |
// This function repeatedly calls the filter to process data, until the entire |
-// source is consumed. The return value from the filter is appended to output. |
+// source is consumed. The return value from the filter is appended to output. |
// This allows us to vary input and output block sizes in order to test for edge |
// effects (boundary effects?) during the filtering process. |
// This function provides data to the filter in blocks of no-more-than the |
-// specified input_block_length. It allows the filter to fill no more than |
+// specified input_block_length. It allows the filter to fill no more than |
// output_buffer_length in any one call to proccess (a.k.a., Read) data, and |
// concatenates all these little output blocks into the singular output string. |
static bool FilterTestData(const std::string& source, |
@@ -159,6 +167,19 @@ static std::string NewSdchDictionary(const std::string& domain) { |
return dictionary; |
} |
+static std::string NewSdchExpiredDictionary(const std::string& domain) { |
+ std::string dictionary; |
+ if (!domain.empty()) { |
+ dictionary.append("Domain: "); |
+ dictionary.append(domain); |
+ dictionary.append("\n"); |
+ } |
+ dictionary.append("Max-Age: 0\n"); |
+ dictionary.append("\n"); |
+ dictionary.append(kTestVcdiffDictionary, sizeof(kTestVcdiffDictionary) - 1); |
+ return dictionary; |
+} |
+ |
//------------------------------------------------------------------------------ |
TEST_F(SdchFilterTest, EmptyInputOk) { |
@@ -197,10 +218,10 @@ TEST_F(SdchFilterTest, SparseContextOk) { |
EXPECT_EQ(0, output_bytes_or_buffer_size); |
EXPECT_EQ(Filter::FILTER_NEED_MORE_DATA, status); |
- // Partially tear down context. Anything that goes through request() |
+ // Partially tear down context. Anything that goes through request() |
// without checking it for null in the URLRequestJob::HttpFilterContext |
- // implementation is suspect. Everything that does check it for null should |
- // return null. This is to test for incorrectly relying on filter_context() |
+ // implementation is suspect. Everything that does check it for null should |
+ // return null. This is to test for incorrectly relying on filter_context() |
// from the SdchFilter destructor. |
filter_context()->NukeUnstableInterfaces(); |
} |
@@ -451,7 +472,7 @@ TEST_F(SdchFilterTest, BasicDictionary) { |
std::vector<Filter::FilterType> filter_types; |
filter_types.push_back(Filter::FILTER_TYPE_SDCH); |
- filter_context()->SetURL(url); |
+ SetupFilterContextWithGURL(url); |
scoped_ptr<Filter> filter(Filter::Factory(filter_types, *filter_context())); |
@@ -488,7 +509,8 @@ TEST_F(SdchFilterTest, NoDecodeHttps) { |
std::vector<Filter::FilterType> filter_types; |
filter_types.push_back(Filter::FILTER_TYPE_SDCH); |
- filter_context()->SetURL(GURL("https://" + kSampleDomain)); |
+ GURL filter_context_gurl("https://" + kSampleDomain); |
+ SetupFilterContextWithGURL(GURL("https://" + kSampleDomain)); |
scoped_ptr<Filter> filter(Filter::Factory(filter_types, *filter_context())); |
const size_t feed_block_size(100); |
@@ -501,7 +523,7 @@ TEST_F(SdchFilterTest, NoDecodeHttps) { |
// Current failsafe TODO/hack refuses to decode any content that doesn't use |
// http as the scheme (see use of DICTIONARY_SELECTED_FOR_NON_HTTP). |
-// The following tests this blockage. Note that blacklisting results, so we |
+// The following tests this blockage. Note that blacklisting results, so we |
// we need separate tests for each of these. |
TEST_F(SdchFilterTest, NoDecodeFtp) { |
// Construct a valid SDCH dictionary from a VCDIFF dictionary. |
@@ -518,7 +540,7 @@ TEST_F(SdchFilterTest, NoDecodeFtp) { |
std::vector<Filter::FilterType> filter_types; |
filter_types.push_back(Filter::FILTER_TYPE_SDCH); |
- filter_context()->SetURL(GURL("ftp://" + kSampleDomain)); |
+ SetupFilterContextWithGURL(GURL("ftp://" + kSampleDomain)); |
scoped_ptr<Filter> filter(Filter::Factory(filter_types, *filter_context())); |
const size_t feed_block_size(100); |
@@ -544,7 +566,7 @@ TEST_F(SdchFilterTest, NoDecodeFileColon) { |
std::vector<Filter::FilterType> filter_types; |
filter_types.push_back(Filter::FILTER_TYPE_SDCH); |
- filter_context()->SetURL(GURL("file://" + kSampleDomain)); |
+ SetupFilterContextWithGURL(GURL("file://" + kSampleDomain)); |
scoped_ptr<Filter> filter(Filter::Factory(filter_types, *filter_context())); |
const size_t feed_block_size(100); |
@@ -570,7 +592,7 @@ TEST_F(SdchFilterTest, NoDecodeAboutColon) { |
std::vector<Filter::FilterType> filter_types; |
filter_types.push_back(Filter::FILTER_TYPE_SDCH); |
- filter_context()->SetURL(GURL("about://" + kSampleDomain)); |
+ SetupFilterContextWithGURL(GURL("about://" + kSampleDomain)); |
scoped_ptr<Filter> filter(Filter::Factory(filter_types, *filter_context())); |
const size_t feed_block_size(100); |
@@ -596,7 +618,7 @@ TEST_F(SdchFilterTest, NoDecodeJavaScript) { |
std::vector<Filter::FilterType> filter_types; |
filter_types.push_back(Filter::FILTER_TYPE_SDCH); |
- filter_context()->SetURL(GURL("javascript://" + kSampleDomain)); |
+ SetupFilterContextWithGURL(GURL("javascript://" + kSampleDomain)); |
scoped_ptr<Filter> filter(Filter::Factory(filter_types, *filter_context())); |
const size_t feed_block_size(100); |
@@ -622,7 +644,7 @@ TEST_F(SdchFilterTest, CanStillDecodeHttp) { |
std::vector<Filter::FilterType> filter_types; |
filter_types.push_back(Filter::FILTER_TYPE_SDCH); |
- filter_context()->SetURL(GURL("http://" + kSampleDomain)); |
+ SetupFilterContextWithGURL(GURL("http://" + kSampleDomain)); |
scoped_ptr<Filter> filter(Filter::Factory(filter_types, *filter_context())); |
const size_t feed_block_size(100); |
@@ -651,7 +673,7 @@ TEST_F(SdchFilterTest, CrossDomainDictionaryUse) { |
// Decode with content arriving from the "wrong" domain. |
// This tests SdchManager::CanSet(). |
GURL wrong_domain_url("http://www.wrongdomain.com"); |
- filter_context()->SetURL(wrong_domain_url); |
+ SetupFilterContextWithGURL(wrong_domain_url); |
scoped_ptr<Filter> filter(Filter::Factory(filter_types, *filter_context())); |
size_t feed_block_size = 100; |
@@ -696,7 +718,7 @@ TEST_F(SdchFilterTest, DictionaryPathValidation) { |
filter_types.push_back(Filter::FILTER_TYPE_SDCH); |
// Test decode the path data, arriving from a valid path. |
- filter_context()->SetURL(GURL(url_string + path)); |
+ SetupFilterContextWithGURL(GURL(url_string + path)); |
scoped_ptr<Filter> filter(Filter::Factory(filter_types, *filter_context())); |
size_t feed_block_size = 100; |
@@ -708,7 +730,7 @@ TEST_F(SdchFilterTest, DictionaryPathValidation) { |
EXPECT_EQ(output, expanded_); |
// Test decode the path data, arriving from a invalid path. |
- filter_context()->SetURL(GURL(url_string)); |
+ SetupFilterContextWithGURL(GURL(url_string)); |
filter.reset(Filter::Factory(filter_types, *filter_context())); |
feed_block_size = 100; |
@@ -753,7 +775,7 @@ TEST_F(SdchFilterTest, DictionaryPortValidation) { |
filter_types.push_back(Filter::FILTER_TYPE_SDCH); |
// Test decode the port data, arriving from a valid port. |
- filter_context()->SetURL(GURL(url_string + ":" + port)); |
+ SetupFilterContextWithGURL(GURL(url_string + ":" + port)); |
scoped_ptr<Filter> filter(Filter::Factory(filter_types, *filter_context())); |
size_t feed_block_size = 100; |
@@ -764,7 +786,7 @@ TEST_F(SdchFilterTest, DictionaryPortValidation) { |
EXPECT_EQ(output, expanded_); |
// Test decode the port data, arriving from a valid (default) port. |
- filter_context()->SetURL(GURL(url_string)); // Default port. |
+ SetupFilterContextWithGURL(GURL(url_string)); // Default port. |
filter.reset(Filter::Factory(filter_types, *filter_context())); |
feed_block_size = 100; |
@@ -775,7 +797,7 @@ TEST_F(SdchFilterTest, DictionaryPortValidation) { |
EXPECT_EQ(output, expanded_); |
// Test decode the port data, arriving from a invalid port. |
- filter_context()->SetURL(GURL(url_string + ":" + port + "1")); |
+ SetupFilterContextWithGURL(GURL(url_string + ":" + port + "1")); |
filter.reset(Filter::Factory(filter_types, *filter_context())); |
feed_block_size = 100; |
@@ -856,8 +878,8 @@ class SdchFilterChainingTest { |
}; |
// Test that filters can be cascaded (chained) so that the output of one filter |
-// is processed by the next one. This is most critical for SDCH, which is |
-// routinely followed by gzip (during encoding). The filter we'll test for will |
+// is processed by the next one. This is most critical for SDCH, which is |
+// routinely followed by gzip (during encoding). The filter we'll test for will |
// do the gzip decoding first, and then decode the SDCH content. |
TEST_F(SdchFilterTest, FilterChaining) { |
// Construct a valid SDCH dictionary from a VCDIFF dictionary. |
@@ -884,7 +906,7 @@ TEST_F(SdchFilterTest, FilterChaining) { |
CHECK_GT(kLargeInputBufferSize, gzip_compressed_sdch.size()); |
CHECK_GT(kLargeInputBufferSize, sdch_compressed.size()); |
CHECK_GT(kLargeInputBufferSize, expanded_.size()); |
- filter_context()->SetURL(url); |
+ SetupFilterContextWithGURL(url); |
scoped_ptr<Filter> filter( |
SdchFilterChainingTest::Factory(filter_types, *filter_context(), |
kLargeInputBufferSize)); |
@@ -962,16 +984,15 @@ TEST_F(SdchFilterTest, DefaultGzipIfSdch) { |
filter_types.push_back(Filter::FILTER_TYPE_SDCH); |
filter_context()->SetMimeType("anything/mime"); |
- filter_context()->SetSdchResponse(true); |
+ SetupFilterContextWithGURL(url); |
+ |
Filter::FixupEncodingTypes(*filter_context(), &filter_types); |
ASSERT_EQ(filter_types.size(), 2u); |
EXPECT_EQ(filter_types[0], Filter::FILTER_TYPE_SDCH); |
EXPECT_EQ(filter_types[1], Filter::FILTER_TYPE_GZIP_HELPING_SDCH); |
// First try with a large buffer (larger than test input, or compressed data). |
- filter_context()->SetURL(url); |
- scoped_ptr<Filter> filter(Filter::Factory(filter_types, *filter_context())); |
- |
+ scoped_ptr<Filter> filter(Filter::Factory(filter_types, *filter_context())); |
// Verify that chained filter is waiting for data. |
char tiny_output_buffer[10]; |
@@ -1020,7 +1041,7 @@ TEST_F(SdchFilterTest, AcceptGzipSdchIfGzip) { |
filter_types.push_back(Filter::FILTER_TYPE_GZIP); |
filter_context()->SetMimeType("anything/mime"); |
- filter_context()->SetSdchResponse(true); |
+ SetupFilterContextWithGURL(url); |
Filter::FixupEncodingTypes(*filter_context(), &filter_types); |
ASSERT_EQ(filter_types.size(), 3u); |
EXPECT_EQ(filter_types[0], Filter::FILTER_TYPE_SDCH_POSSIBLE); |
@@ -1028,10 +1049,8 @@ TEST_F(SdchFilterTest, AcceptGzipSdchIfGzip) { |
EXPECT_EQ(filter_types[2], Filter::FILTER_TYPE_GZIP); |
// First try with a large buffer (larger than test input, or compressed data). |
- filter_context()->SetURL(url); |
scoped_ptr<Filter> filter(Filter::Factory(filter_types, *filter_context())); |
- |
// Verify that chained filter is waiting for data. |
char tiny_output_buffer[10]; |
int tiny_output_size = sizeof(tiny_output_buffer); |
@@ -1077,17 +1096,15 @@ TEST_F(SdchFilterTest, DefaultSdchGzipIfEmpty) { |
std::vector<Filter::FilterType> filter_types; |
filter_context()->SetMimeType("anything/mime"); |
- filter_context()->SetSdchResponse(true); |
+ SetupFilterContextWithGURL(url); |
Filter::FixupEncodingTypes(*filter_context(), &filter_types); |
ASSERT_EQ(filter_types.size(), 2u); |
EXPECT_EQ(filter_types[0], Filter::FILTER_TYPE_SDCH_POSSIBLE); |
EXPECT_EQ(filter_types[1], Filter::FILTER_TYPE_GZIP_HELPING_SDCH); |
// First try with a large buffer (larger than test input, or compressed data). |
- filter_context()->SetURL(url); |
scoped_ptr<Filter> filter(Filter::Factory(filter_types, *filter_context())); |
- |
// Verify that chained filter is waiting for data. |
char tiny_output_buffer[10]; |
int tiny_output_size = sizeof(tiny_output_buffer); |
@@ -1138,7 +1155,7 @@ TEST_F(SdchFilterTest, AcceptGzipGzipSdchIfGzip) { |
filter_types.push_back(Filter::FILTER_TYPE_GZIP); |
filter_context()->SetMimeType("anything/mime"); |
- filter_context()->SetSdchResponse(true); |
+ SetupFilterContextWithGURL(url); |
Filter::FixupEncodingTypes(*filter_context(), &filter_types); |
ASSERT_EQ(filter_types.size(), 3u); |
EXPECT_EQ(filter_types[0], Filter::FILTER_TYPE_SDCH_POSSIBLE); |
@@ -1146,7 +1163,6 @@ TEST_F(SdchFilterTest, AcceptGzipGzipSdchIfGzip) { |
EXPECT_EQ(filter_types[2], Filter::FILTER_TYPE_GZIP); |
// First try with a large buffer (larger than test input, or compressed data). |
- filter_context()->SetURL(url); |
scoped_ptr<Filter> filter(Filter::Factory(filter_types, *filter_context())); |
// Verify that chained filter is waiting for data. |
@@ -1173,4 +1189,58 @@ TEST_F(SdchFilterTest, AcceptGzipGzipSdchIfGzip) { |
EXPECT_EQ(output, expanded_); |
} |
+// Test to make sure we decode properly with an unexpected dictionary. |
+TEST_F(SdchFilterTest, UnexpectedDictionary) { |
+ // Setup a dictionary, add it to the filter context, and create a filter |
+ // based on that dictionary. |
+ const std::string kSampleDomain = "sdchtest.com"; |
+ std::string dictionary(NewSdchDictionary(kSampleDomain)); |
+ std::string url_string = "http://" + kSampleDomain; |
+ GURL url(url_string); |
+ EXPECT_TRUE(AddSdchDictionary(dictionary, url)); |
+ |
+ SetupFilterContextWithGURL(url); |
+ |
+ std::vector<Filter::FilterType> filter_types; |
+ filter_types.push_back(Filter::FILTER_TYPE_SDCH); |
+ scoped_ptr<Filter> filter(Filter::Factory(filter_types, *filter_context())); |
+ |
+ // Setup another dictionary, expired. Don't add it to the filter context. |
+ // Delete stored dictionaries first to handle platforms which only |
+ // have room for a single dictionary. |
+ sdch_manager_->ClearData(); |
+ std::string expired_dictionary(NewSdchExpiredDictionary(kSampleDomain)); |
+ |
+ // Don't use the Helper function since its insertion check is indeterminate |
+ // for a Max-Age: 0 dictionary. |
+ sdch_manager_->AddSdchDictionary(expired_dictionary, url); |
+ |
+ std::string client_hash; |
+ std::string server_hash; |
+ SdchManager::GenerateHash(expired_dictionary, &client_hash, &server_hash); |
+ |
+ // Make sure Max-Age: 0 shows up as expired. |
+ scoped_ptr<base::SimpleTestClock> clock(new base::SimpleTestClock); |
+ clock->SetNow(base::Time::Now()); |
+ clock->Advance(base::TimeDelta::FromMinutes(5)); |
+ SdchProblemCode problem_code; |
+ scoped_ptr<SdchManager::DictionarySet> hash_set( |
+ sdch_manager_->GetDictionarySetByHash( |
+ url, server_hash, &problem_code).Pass()); |
+ ASSERT_TRUE(hash_set); |
+ ASSERT_EQ(SDCH_OK, problem_code); |
+ |
+ const_cast<SdchManager::Dictionary*>( |
+ hash_set->GetDictionary(server_hash))->SetClockForTesting( |
+ clock.Pass()); |
+ |
+ // Encode output with the second dictionary. |
+ std::string sdch_compressed(NewSdchCompressedData(expired_dictionary)); |
+ |
+ // See if the filter decodes it. |
+ std::string output; |
+ EXPECT_TRUE(FilterTestData(sdch_compressed, 100, 100, filter.get(), &output)); |
+ EXPECT_EQ(expanded_, output); |
+} |
+ |
} // namespace net |