| // Copyright 2014 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "net/base/io_buffer.h" |
| #include "net/filter/filter.h" |
| #include "net/filter/mock_filter_context.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| namespace net { |
| |
| namespace { |
| |
| class PassThroughFilter : public Filter { |
| public: |
| PassThroughFilter() : Filter(FILTER_TYPE_UNSUPPORTED) {} |
| |
| FilterStatus ReadFilteredData(char* dest_buffer, int* dest_len) override { |
| return CopyOut(dest_buffer, dest_len); |
| } |
| |
| DISALLOW_COPY_AND_ASSIGN(PassThroughFilter); |
| }; |
| |
| } // namespace |
| |
| TEST(FilterTest, ContentTypeId) { |
| // Check for basic translation of Content-Encoding, including case variations. |
| EXPECT_EQ(Filter::FILTER_TYPE_DEFLATE, |
| Filter::ConvertEncodingToType("deflate")); |
| EXPECT_EQ(Filter::FILTER_TYPE_DEFLATE, |
| Filter::ConvertEncodingToType("deflAte")); |
| EXPECT_EQ(Filter::FILTER_TYPE_GZIP, |
| Filter::ConvertEncodingToType("gzip")); |
| EXPECT_EQ(Filter::FILTER_TYPE_GZIP, |
| Filter::ConvertEncodingToType("GzIp")); |
| EXPECT_EQ(Filter::FILTER_TYPE_GZIP, |
| Filter::ConvertEncodingToType("x-gzip")); |
| EXPECT_EQ(Filter::FILTER_TYPE_GZIP, |
| Filter::ConvertEncodingToType("X-GzIp")); |
| EXPECT_EQ(Filter::FILTER_TYPE_SDCH, |
| Filter::ConvertEncodingToType("sdch")); |
| EXPECT_EQ(Filter::FILTER_TYPE_SDCH, |
| Filter::ConvertEncodingToType("sDcH")); |
| EXPECT_EQ(Filter::FILTER_TYPE_UNSUPPORTED, |
| Filter::ConvertEncodingToType("weird")); |
| EXPECT_EQ(Filter::FILTER_TYPE_UNSUPPORTED, |
| Filter::ConvertEncodingToType("strange")); |
| } |
| |
| TEST(FilterTest, SdchEncoding) { |
| // Handle content encodings including SDCH. |
| const std::string kTextHtmlMime("text/html"); |
| MockFilterContext filter_context; |
| // Empty handle indicates to filter that SDCH is active. |
| filter_context.SetSdchResponse( |
| SdchManager::CreateEmptyDictionarySetForTesting().Pass()); |
| |
| std::vector<Filter::FilterType> encoding_types; |
| |
| // Check for most common encoding, and verify it survives unchanged. |
| encoding_types.clear(); |
| encoding_types.push_back(Filter::FILTER_TYPE_SDCH); |
| encoding_types.push_back(Filter::FILTER_TYPE_GZIP); |
| filter_context.SetMimeType(kTextHtmlMime); |
| Filter::FixupEncodingTypes(filter_context, &encoding_types); |
| ASSERT_EQ(2U, encoding_types.size()); |
| EXPECT_EQ(Filter::FILTER_TYPE_SDCH, encoding_types[0]); |
| EXPECT_EQ(Filter::FILTER_TYPE_GZIP, encoding_types[1]); |
| |
| // Unchanged even with other mime types. |
| encoding_types.clear(); |
| encoding_types.push_back(Filter::FILTER_TYPE_SDCH); |
| encoding_types.push_back(Filter::FILTER_TYPE_GZIP); |
| filter_context.SetMimeType("other/type"); |
| Filter::FixupEncodingTypes(filter_context, &encoding_types); |
| ASSERT_EQ(2U, encoding_types.size()); |
| EXPECT_EQ(Filter::FILTER_TYPE_SDCH, encoding_types[0]); |
| EXPECT_EQ(Filter::FILTER_TYPE_GZIP, encoding_types[1]); |
| |
| // Solo SDCH is extended to include optional gunzip. |
| encoding_types.clear(); |
| encoding_types.push_back(Filter::FILTER_TYPE_SDCH); |
| Filter::FixupEncodingTypes(filter_context, &encoding_types); |
| ASSERT_EQ(2U, encoding_types.size()); |
| EXPECT_EQ(Filter::FILTER_TYPE_SDCH, encoding_types[0]); |
| EXPECT_EQ(Filter::FILTER_TYPE_GZIP_HELPING_SDCH, encoding_types[1]); |
| } |
| |
| TEST(FilterTest, MissingSdchEncoding) { |
| // Handle interesting case where entire SDCH encoding assertion "got lost." |
| const std::string kTextHtmlMime("text/html"); |
| MockFilterContext filter_context; |
| filter_context.SetSdchResponse( |
| SdchManager::CreateEmptyDictionarySetForTesting().Pass()); |
| |
| std::vector<Filter::FilterType> encoding_types; |
| |
| // Loss of encoding, but it was an SDCH response with html type. |
| encoding_types.clear(); |
| filter_context.SetMimeType(kTextHtmlMime); |
| Filter::FixupEncodingTypes(filter_context, &encoding_types); |
| ASSERT_EQ(2U, encoding_types.size()); |
| EXPECT_EQ(Filter::FILTER_TYPE_SDCH_POSSIBLE, encoding_types[0]); |
| EXPECT_EQ(Filter::FILTER_TYPE_GZIP_HELPING_SDCH, encoding_types[1]); |
| |
| // Loss of encoding, but it was an SDCH response with a prefix that says it |
| // was an html type. Note that it *should* be the case that a precise match |
| // with "text/html" we be collected by GetMimeType() and passed in, but we |
| // coded the fixup defensively (scanning for a prefix of "text/html", so this |
| // is an example which could survive such confusion in the caller). |
| encoding_types.clear(); |
| filter_context.SetMimeType("text/html; charset=UTF-8"); |
| Filter::FixupEncodingTypes(filter_context, &encoding_types); |
| ASSERT_EQ(2U, encoding_types.size()); |
| EXPECT_EQ(Filter::FILTER_TYPE_SDCH_POSSIBLE, encoding_types[0]); |
| EXPECT_EQ(Filter::FILTER_TYPE_GZIP_HELPING_SDCH, encoding_types[1]); |
| |
| // No encoding, but it was an SDCH response with non-html type. |
| encoding_types.clear(); |
| filter_context.SetMimeType("other/mime"); |
| Filter::FixupEncodingTypes(filter_context, &encoding_types); |
| ASSERT_EQ(2U, encoding_types.size()); |
| EXPECT_EQ(Filter::FILTER_TYPE_SDCH_POSSIBLE, encoding_types[0]); |
| EXPECT_EQ(Filter::FILTER_TYPE_GZIP_HELPING_SDCH, encoding_types[1]); |
| } |
| |
| // FixupEncodingTypes() should leave gzip encoding intact. |
| TEST(FilterTest, Gzip) { |
| const std::string kUrl("http://example.com/foo"); |
| MockFilterContext filter_context; |
| std::vector<Filter::FilterType> encoding_types; |
| filter_context.SetURL(GURL(kUrl)); |
| |
| Filter::FixupEncodingTypes(filter_context, &encoding_types); |
| EXPECT_EQ(0U, encoding_types.size()); |
| |
| encoding_types.clear(); |
| encoding_types.push_back(Filter::FILTER_TYPE_GZIP); |
| Filter::FixupEncodingTypes(filter_context, &encoding_types); |
| ASSERT_EQ(1U, encoding_types.size()); |
| EXPECT_EQ(Filter::FILTER_TYPE_GZIP, encoding_types.front()); |
| } |
| |
| // Make sure a series of three pass-through filters copies the data cleanly. |
| // Regression test for http://crbug.com/418975. |
| TEST(FilterTest, ThreeFilterChain) { |
| scoped_ptr<PassThroughFilter> filter1(new PassThroughFilter); |
| scoped_ptr<PassThroughFilter> filter2(new PassThroughFilter); |
| scoped_ptr<PassThroughFilter> filter3(new PassThroughFilter); |
| |
| filter1->InitBuffer(32 * 1024); |
| filter2->InitBuffer(32 * 1024); |
| filter3->InitBuffer(32 * 1024); |
| |
| filter2->next_filter_ = filter3.Pass(); |
| filter1->next_filter_ = filter2.Pass(); |
| |
| // Initialize the input array with a varying byte sequence. |
| const size_t input_array_size = 64 * 1024; |
| char input_array[input_array_size]; |
| size_t read_array_index = 0; |
| for (size_t i = 0; i < input_array_size; i++) { |
| input_array[i] = i % 113; |
| } |
| |
| const size_t output_array_size = 4 * 1024; |
| char output_array[output_array_size]; |
| |
| size_t compare_array_index = 0; |
| |
| do { |
| // Read data from the filter chain. |
| int amount_read = output_array_size; |
| Filter::FilterStatus status = filter1->ReadData(output_array, &amount_read); |
| EXPECT_NE(Filter::FILTER_ERROR, status); |
| EXPECT_EQ(0, memcmp(output_array, input_array + compare_array_index, |
| amount_read)); |
| compare_array_index += amount_read; |
| |
| // Detect the various indications that data transfer along the chain is |
| // complete. |
| if (Filter::FILTER_DONE == status || Filter::FILTER_ERROR == status || |
| (Filter::FILTER_OK == status && amount_read == 0) || |
| (Filter::FILTER_NEED_MORE_DATA == status && |
| read_array_index == input_array_size)) |
| break; |
| |
| if (Filter::FILTER_OK == status) |
| continue; |
| |
| // Write needed data into the filter chain. |
| ASSERT_EQ(Filter::FILTER_NEED_MORE_DATA, status); |
| ASSERT_NE(0, filter1->stream_buffer_size()); |
| size_t amount_to_copy = std::min( |
| static_cast<size_t>(filter1->stream_buffer_size()), |
| input_array_size - read_array_index); |
| memcpy(filter1->stream_buffer()->data(), |
| input_array + read_array_index, |
| amount_to_copy); |
| filter1->FlushStreamBuffer(amount_to_copy); |
| read_array_index += amount_to_copy; |
| } while (true); |
| |
| EXPECT_EQ(read_array_index, input_array_size); |
| EXPECT_EQ(compare_array_index, input_array_size); |
| } |
| |
| } // Namespace net |