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.
5 #include "net/base/io_buffer.h"
6 #include "net/filter/filter.h"
7 #include "net/filter/mock_filter_context.h"
8 #include "testing/gtest/include/gtest/gtest.h"
14 class PassThroughFilter
: public Filter
{
16 PassThroughFilter() : Filter(FILTER_TYPE_UNSUPPORTED
) {}
18 FilterStatus
ReadFilteredData(char* dest_buffer
, int* dest_len
) override
{
19 return CopyOut(dest_buffer
, dest_len
);
22 DISALLOW_COPY_AND_ASSIGN(PassThroughFilter
);
27 TEST(FilterTest
, ContentTypeId
) {
28 // Check for basic translation of Content-Encoding, including case variations.
29 EXPECT_EQ(Filter::FILTER_TYPE_DEFLATE
,
30 Filter::ConvertEncodingToType("deflate"));
31 EXPECT_EQ(Filter::FILTER_TYPE_DEFLATE
,
32 Filter::ConvertEncodingToType("deflAte"));
33 EXPECT_EQ(Filter::FILTER_TYPE_GZIP
,
34 Filter::ConvertEncodingToType("gzip"));
35 EXPECT_EQ(Filter::FILTER_TYPE_GZIP
,
36 Filter::ConvertEncodingToType("GzIp"));
37 EXPECT_EQ(Filter::FILTER_TYPE_GZIP
,
38 Filter::ConvertEncodingToType("x-gzip"));
39 EXPECT_EQ(Filter::FILTER_TYPE_GZIP
,
40 Filter::ConvertEncodingToType("X-GzIp"));
41 EXPECT_EQ(Filter::FILTER_TYPE_SDCH
,
42 Filter::ConvertEncodingToType("sdch"));
43 EXPECT_EQ(Filter::FILTER_TYPE_SDCH
,
44 Filter::ConvertEncodingToType("sDcH"));
45 EXPECT_EQ(Filter::FILTER_TYPE_UNSUPPORTED
,
46 Filter::ConvertEncodingToType("weird"));
47 EXPECT_EQ(Filter::FILTER_TYPE_UNSUPPORTED
,
48 Filter::ConvertEncodingToType("strange"));
51 // Check various fixups that modify content encoding lists.
52 TEST(FilterTest
, ApacheGzip
) {
53 MockFilterContext filter_context
;
54 filter_context
.SetSdchResponse(NULL
);
56 // Check that redundant gzip mime type removes only solo gzip encoding.
57 const std::string
kGzipMime1("application/x-gzip");
58 const std::string
kGzipMime2("application/gzip");
59 const std::string
kGzipMime3("application/x-gunzip");
60 std::vector
<Filter::FilterType
> encoding_types
;
62 // First show it removes the gzip, given any gzip style mime type.
63 encoding_types
.clear();
64 encoding_types
.push_back(Filter::FILTER_TYPE_GZIP
);
65 filter_context
.SetMimeType(kGzipMime1
);
66 Filter::FixupEncodingTypes(filter_context
, &encoding_types
);
67 EXPECT_TRUE(encoding_types
.empty());
69 encoding_types
.clear();
70 encoding_types
.push_back(Filter::FILTER_TYPE_GZIP
);
71 filter_context
.SetMimeType(kGzipMime2
);
72 Filter::FixupEncodingTypes(filter_context
, &encoding_types
);
73 EXPECT_TRUE(encoding_types
.empty());
75 encoding_types
.clear();
76 encoding_types
.push_back(Filter::FILTER_TYPE_GZIP
);
77 filter_context
.SetMimeType(kGzipMime3
);
78 Filter::FixupEncodingTypes(filter_context
, &encoding_types
);
79 EXPECT_TRUE(encoding_types
.empty());
81 // Check to be sure it doesn't remove everything when it has such a type.
82 encoding_types
.clear();
83 encoding_types
.push_back(Filter::FILTER_TYPE_SDCH
);
84 filter_context
.SetMimeType(kGzipMime1
);
85 Filter::FixupEncodingTypes(filter_context
, &encoding_types
);
86 ASSERT_EQ(1U, encoding_types
.size());
87 EXPECT_EQ(Filter::FILTER_TYPE_SDCH
, encoding_types
.front());
89 // Check to be sure that gzip can survive with other mime types.
90 encoding_types
.clear();
91 encoding_types
.push_back(Filter::FILTER_TYPE_GZIP
);
92 filter_context
.SetMimeType("other/mime");
93 Filter::FixupEncodingTypes(filter_context
, &encoding_types
);
94 ASSERT_EQ(1U, encoding_types
.size());
95 EXPECT_EQ(Filter::FILTER_TYPE_GZIP
, encoding_types
.front());
98 TEST(FilterTest
, GzipContentDispositionFilename
) {
99 MockFilterContext filter_context
;
100 filter_context
.SetSdchResponse(NULL
);
102 const std::string
kGzipMime("application/x-tar");
103 const std::string
kContentDisposition("attachment; filename=\"foo.tgz\"");
104 const std::string
kURL("http://foo.com/getfoo.php");
105 std::vector
<Filter::FilterType
> encoding_types
;
107 encoding_types
.push_back(Filter::FILTER_TYPE_GZIP
);
108 filter_context
.SetMimeType(kGzipMime
);
109 filter_context
.SetURL(GURL(kURL
));
110 filter_context
.SetContentDisposition(kContentDisposition
);
111 Filter::FixupEncodingTypes(filter_context
, &encoding_types
);
112 ASSERT_EQ(0U, encoding_types
.size());
115 TEST(FilterTest
, SdchEncoding
) {
116 // Handle content encodings including SDCH.
117 const std::string
kTextHtmlMime("text/html");
118 MockFilterContext filter_context
;
119 // Empty handle indicates to filter that SDCH is active.
120 filter_context
.SetSdchResponse(
121 SdchManager::CreateEmptyDictionarySetForTesting().Pass());
123 std::vector
<Filter::FilterType
> encoding_types
;
125 // Check for most common encoding, and verify it survives unchanged.
126 encoding_types
.clear();
127 encoding_types
.push_back(Filter::FILTER_TYPE_SDCH
);
128 encoding_types
.push_back(Filter::FILTER_TYPE_GZIP
);
129 filter_context
.SetMimeType(kTextHtmlMime
);
130 Filter::FixupEncodingTypes(filter_context
, &encoding_types
);
131 ASSERT_EQ(2U, encoding_types
.size());
132 EXPECT_EQ(Filter::FILTER_TYPE_SDCH
, encoding_types
[0]);
133 EXPECT_EQ(Filter::FILTER_TYPE_GZIP
, encoding_types
[1]);
135 // Unchanged even with other mime types.
136 encoding_types
.clear();
137 encoding_types
.push_back(Filter::FILTER_TYPE_SDCH
);
138 encoding_types
.push_back(Filter::FILTER_TYPE_GZIP
);
139 filter_context
.SetMimeType("other/type");
140 Filter::FixupEncodingTypes(filter_context
, &encoding_types
);
141 ASSERT_EQ(2U, encoding_types
.size());
142 EXPECT_EQ(Filter::FILTER_TYPE_SDCH
, encoding_types
[0]);
143 EXPECT_EQ(Filter::FILTER_TYPE_GZIP
, encoding_types
[1]);
145 // Solo SDCH is extended to include optional gunzip.
146 encoding_types
.clear();
147 encoding_types
.push_back(Filter::FILTER_TYPE_SDCH
);
148 Filter::FixupEncodingTypes(filter_context
, &encoding_types
);
149 ASSERT_EQ(2U, encoding_types
.size());
150 EXPECT_EQ(Filter::FILTER_TYPE_SDCH
, encoding_types
[0]);
151 EXPECT_EQ(Filter::FILTER_TYPE_GZIP_HELPING_SDCH
, encoding_types
[1]);
154 TEST(FilterTest
, MissingSdchEncoding
) {
155 // Handle interesting case where entire SDCH encoding assertion "got lost."
156 const std::string
kTextHtmlMime("text/html");
157 MockFilterContext filter_context
;
158 filter_context
.SetSdchResponse(
159 SdchManager::CreateEmptyDictionarySetForTesting().Pass());
161 std::vector
<Filter::FilterType
> encoding_types
;
163 // Loss of encoding, but it was an SDCH response with html type.
164 encoding_types
.clear();
165 filter_context
.SetMimeType(kTextHtmlMime
);
166 Filter::FixupEncodingTypes(filter_context
, &encoding_types
);
167 ASSERT_EQ(2U, encoding_types
.size());
168 EXPECT_EQ(Filter::FILTER_TYPE_SDCH_POSSIBLE
, encoding_types
[0]);
169 EXPECT_EQ(Filter::FILTER_TYPE_GZIP_HELPING_SDCH
, encoding_types
[1]);
171 // Loss of encoding, but it was an SDCH response with a prefix that says it
172 // was an html type. Note that it *should* be the case that a precise match
173 // with "text/html" we be collected by GetMimeType() and passed in, but we
174 // coded the fixup defensively (scanning for a prefix of "text/html", so this
175 // is an example which could survive such confusion in the caller).
176 encoding_types
.clear();
177 filter_context
.SetMimeType("text/html; charset=UTF-8");
178 Filter::FixupEncodingTypes(filter_context
, &encoding_types
);
179 ASSERT_EQ(2U, encoding_types
.size());
180 EXPECT_EQ(Filter::FILTER_TYPE_SDCH_POSSIBLE
, encoding_types
[0]);
181 EXPECT_EQ(Filter::FILTER_TYPE_GZIP_HELPING_SDCH
, encoding_types
[1]);
183 // No encoding, but it was an SDCH response with non-html type.
184 encoding_types
.clear();
185 filter_context
.SetMimeType("other/mime");
186 Filter::FixupEncodingTypes(filter_context
, &encoding_types
);
187 ASSERT_EQ(2U, encoding_types
.size());
188 EXPECT_EQ(Filter::FILTER_TYPE_SDCH_POSSIBLE
, encoding_types
[0]);
189 EXPECT_EQ(Filter::FILTER_TYPE_GZIP_HELPING_SDCH
, encoding_types
[1]);
192 TEST(FilterTest
, Svgz
) {
193 MockFilterContext filter_context
;
195 // Check that svgz files are only decompressed when not downloading.
196 const std::string
kSvgzMime("image/svg+xml");
197 const std::string
kSvgzUrl("http://ignore.com/foo.svgz");
198 const std::string
kSvgUrl("http://ignore.com/foo.svg");
199 std::vector
<Filter::FilterType
> encoding_types
;
201 // Test svgz extension
202 encoding_types
.clear();
203 encoding_types
.push_back(Filter::FILTER_TYPE_GZIP
);
204 filter_context
.SetDownload(false);
205 filter_context
.SetMimeType(kSvgzMime
);
206 filter_context
.SetURL(GURL(kSvgzUrl
));
207 Filter::FixupEncodingTypes(filter_context
, &encoding_types
);
208 ASSERT_EQ(1U, encoding_types
.size());
209 EXPECT_EQ(Filter::FILTER_TYPE_GZIP
, encoding_types
.front());
211 encoding_types
.clear();
212 encoding_types
.push_back(Filter::FILTER_TYPE_GZIP
);
213 filter_context
.SetDownload(true);
214 filter_context
.SetMimeType(kSvgzMime
);
215 filter_context
.SetURL(GURL(kSvgzUrl
));
216 Filter::FixupEncodingTypes(filter_context
, &encoding_types
);
217 EXPECT_TRUE(encoding_types
.empty());
219 // Test svg extension
220 encoding_types
.clear();
221 encoding_types
.push_back(Filter::FILTER_TYPE_GZIP
);
222 filter_context
.SetDownload(false);
223 filter_context
.SetMimeType(kSvgzMime
);
224 filter_context
.SetURL(GURL(kSvgUrl
));
225 Filter::FixupEncodingTypes(filter_context
, &encoding_types
);
226 ASSERT_EQ(1U, encoding_types
.size());
227 EXPECT_EQ(Filter::FILTER_TYPE_GZIP
, encoding_types
.front());
229 encoding_types
.clear();
230 encoding_types
.push_back(Filter::FILTER_TYPE_GZIP
);
231 filter_context
.SetDownload(true);
232 filter_context
.SetMimeType(kSvgzMime
);
233 filter_context
.SetURL(GURL(kSvgUrl
));
234 Filter::FixupEncodingTypes(filter_context
, &encoding_types
);
235 ASSERT_EQ(1U, encoding_types
.size());
236 EXPECT_EQ(Filter::FILTER_TYPE_GZIP
, encoding_types
.front());
239 TEST(FilterTest
, UnsupportedMimeGzip
) {
240 // From issue 8170 - handling files with Content-Encoding: x-gzip
241 MockFilterContext filter_context
;
242 std::vector
<Filter::FilterType
> encoding_types
;
243 const std::string
kTarMime("application/x-tar");
244 const std::string
kCpioMime("application/x-cpio");
245 const std::string
kTarUrl("http://ignore.com/foo.tar");
246 const std::string
kTargzUrl("http://ignore.com/foo.tar.gz");
247 const std::string
kTgzUrl("http://ignore.com/foo.tgz");
248 const std::string
kBadTgzUrl("http://ignore.com/foo.targz");
249 const std::string
kUrl("http://ignore.com/foo");
251 // Firefox 3 does not decompress when we have unsupported mime types for
252 // certain filenames.
253 encoding_types
.clear();
254 encoding_types
.push_back(Filter::FILTER_TYPE_GZIP
);
255 filter_context
.SetDownload(false);
256 filter_context
.SetMimeType(kTarMime
);
257 filter_context
.SetURL(GURL(kTargzUrl
));
258 Filter::FixupEncodingTypes(filter_context
, &encoding_types
);
259 EXPECT_TRUE(encoding_types
.empty());
261 encoding_types
.clear();
262 encoding_types
.push_back(Filter::FILTER_TYPE_GZIP
);
263 filter_context
.SetDownload(false);
264 filter_context
.SetMimeType(kTarMime
);
265 filter_context
.SetURL(GURL(kTgzUrl
));
266 Filter::FixupEncodingTypes(filter_context
, &encoding_types
);
267 EXPECT_TRUE(encoding_types
.empty());
269 encoding_types
.clear();
270 encoding_types
.push_back(Filter::FILTER_TYPE_GZIP
);
271 filter_context
.SetDownload(false);
272 filter_context
.SetMimeType(kCpioMime
);
273 filter_context
.SetURL(GURL(kTgzUrl
));
274 Filter::FixupEncodingTypes(filter_context
, &encoding_types
);
275 EXPECT_TRUE(encoding_types
.empty());
277 // Same behavior for downloads.
278 encoding_types
.clear();
279 encoding_types
.push_back(Filter::FILTER_TYPE_GZIP
);
280 filter_context
.SetDownload(true);
281 filter_context
.SetMimeType(kCpioMime
);
282 filter_context
.SetURL(GURL(kTgzUrl
));
283 Filter::FixupEncodingTypes(filter_context
, &encoding_types
);
284 EXPECT_TRUE(encoding_types
.empty());
286 // Unsupported mime type with wrong file name, decompressed.
287 encoding_types
.clear();
288 encoding_types
.push_back(Filter::FILTER_TYPE_GZIP
);
289 filter_context
.SetDownload(false);
290 filter_context
.SetMimeType(kTarMime
);
291 filter_context
.SetURL(GURL(kUrl
));
292 Filter::FixupEncodingTypes(filter_context
, &encoding_types
);
293 ASSERT_EQ(1U, encoding_types
.size());
294 EXPECT_EQ(Filter::FILTER_TYPE_GZIP
, encoding_types
.front());
296 encoding_types
.clear();
297 encoding_types
.push_back(Filter::FILTER_TYPE_GZIP
);
298 filter_context
.SetDownload(false);
299 filter_context
.SetMimeType(kTarMime
);
300 filter_context
.SetURL(GURL(kTarUrl
));
301 Filter::FixupEncodingTypes(filter_context
, &encoding_types
);
302 ASSERT_EQ(1U, encoding_types
.size());
303 EXPECT_EQ(Filter::FILTER_TYPE_GZIP
, encoding_types
.front());
305 encoding_types
.clear();
306 encoding_types
.push_back(Filter::FILTER_TYPE_GZIP
);
307 filter_context
.SetDownload(false);
308 filter_context
.SetMimeType(kTarMime
);
309 filter_context
.SetURL(GURL(kBadTgzUrl
));
310 Filter::FixupEncodingTypes(filter_context
, &encoding_types
);
311 ASSERT_EQ(1U, encoding_types
.size());
312 EXPECT_EQ(Filter::FILTER_TYPE_GZIP
, encoding_types
.front());
314 // Same behavior for downloads.
315 encoding_types
.clear();
316 encoding_types
.push_back(Filter::FILTER_TYPE_GZIP
);
317 filter_context
.SetDownload(true);
318 filter_context
.SetMimeType(kTarMime
);
319 filter_context
.SetURL(GURL(kBadTgzUrl
));
320 Filter::FixupEncodingTypes(filter_context
, &encoding_types
);
321 ASSERT_EQ(1U, encoding_types
.size());
322 EXPECT_EQ(Filter::FILTER_TYPE_GZIP
, encoding_types
.front());
325 TEST(FilterTest
, SupportedMimeGzip
) {
326 // From issue 16430 - Files with supported mime types should be decompressed,
327 // even though these files end in .gz/.tgz.
328 MockFilterContext filter_context
;
329 std::vector
<Filter::FilterType
> encoding_types
;
330 const std::string
kGzUrl("http://ignore.com/foo.gz");
331 const std::string
kUrl("http://ignore.com/foo");
332 const std::string
kHtmlMime("text/html");
333 const std::string
kJavascriptMime("text/javascript");
335 // For files that does not end in .gz/.tgz, we always decompress.
336 encoding_types
.clear();
337 encoding_types
.push_back(Filter::FILTER_TYPE_GZIP
);
338 filter_context
.SetDownload(false);
339 filter_context
.SetMimeType(kHtmlMime
);
340 filter_context
.SetURL(GURL(kUrl
));
341 Filter::FixupEncodingTypes(filter_context
, &encoding_types
);
342 ASSERT_EQ(1U, encoding_types
.size());
343 EXPECT_EQ(Filter::FILTER_TYPE_GZIP
, encoding_types
.front());
345 encoding_types
.clear();
346 encoding_types
.push_back(Filter::FILTER_TYPE_GZIP
);
347 filter_context
.SetDownload(true);
348 filter_context
.SetMimeType(kHtmlMime
);
349 filter_context
.SetURL(GURL(kUrl
));
350 Filter::FixupEncodingTypes(filter_context
, &encoding_types
);
351 ASSERT_EQ(1U, encoding_types
.size());
352 EXPECT_EQ(Filter::FILTER_TYPE_GZIP
, encoding_types
.front());
354 // And also decompress files that end in .gz/.tgz.
355 encoding_types
.clear();
356 encoding_types
.push_back(Filter::FILTER_TYPE_GZIP
);
357 filter_context
.SetDownload(false);
358 filter_context
.SetMimeType(kHtmlMime
);
359 filter_context
.SetURL(GURL(kGzUrl
));
360 Filter::FixupEncodingTypes(filter_context
, &encoding_types
);
361 ASSERT_EQ(1U, encoding_types
.size());
362 EXPECT_EQ(Filter::FILTER_TYPE_GZIP
, encoding_types
.front());
364 encoding_types
.clear();
365 encoding_types
.push_back(Filter::FILTER_TYPE_GZIP
);
366 filter_context
.SetDownload(false);
367 filter_context
.SetMimeType(kJavascriptMime
);
368 filter_context
.SetURL(GURL(kGzUrl
));
369 Filter::FixupEncodingTypes(filter_context
, &encoding_types
);
370 ASSERT_EQ(1U, encoding_types
.size());
371 EXPECT_EQ(Filter::FILTER_TYPE_GZIP
, encoding_types
.front());
373 // Except on downloads, where they just get saved.
374 encoding_types
.clear();
375 encoding_types
.push_back(Filter::FILTER_TYPE_GZIP
);
376 filter_context
.SetDownload(true);
377 filter_context
.SetMimeType(kHtmlMime
);
378 filter_context
.SetURL(GURL(kGzUrl
));
379 Filter::FixupEncodingTypes(filter_context
, &encoding_types
);
380 EXPECT_TRUE(encoding_types
.empty());
383 // Make sure a series of three pass-through filters copies the data cleanly.
384 // Regression test for http://crbug.com/418975.
385 TEST(FilterTest
, ThreeFilterChain
) {
386 scoped_ptr
<PassThroughFilter
> filter1(new PassThroughFilter
);
387 scoped_ptr
<PassThroughFilter
> filter2(new PassThroughFilter
);
388 scoped_ptr
<PassThroughFilter
> filter3(new PassThroughFilter
);
390 filter1
->InitBuffer(32 * 1024);
391 filter2
->InitBuffer(32 * 1024);
392 filter3
->InitBuffer(32 * 1024);
394 filter2
->next_filter_
= filter3
.Pass();
395 filter1
->next_filter_
= filter2
.Pass();
397 // Initialize the input array with a varying byte sequence.
398 const size_t input_array_size
= 64 * 1024;
399 char input_array
[input_array_size
];
400 size_t read_array_index
= 0;
401 for (size_t i
= 0; i
< input_array_size
; i
++) {
402 input_array
[i
] = i
% 113;
405 const size_t output_array_size
= 4 * 1024;
406 char output_array
[output_array_size
];
408 size_t compare_array_index
= 0;
411 // Read data from the filter chain.
412 int amount_read
= output_array_size
;
413 Filter::FilterStatus status
= filter1
->ReadData(output_array
, &amount_read
);
414 EXPECT_NE(Filter::FILTER_ERROR
, status
);
415 EXPECT_EQ(0, memcmp(output_array
, input_array
+ compare_array_index
,
417 compare_array_index
+= amount_read
;
419 // Detect the various indications that data transfer along the chain is
421 if (Filter::FILTER_DONE
== status
|| Filter::FILTER_ERROR
== status
||
422 (Filter::FILTER_OK
== status
&& amount_read
== 0) ||
423 (Filter::FILTER_NEED_MORE_DATA
== status
&&
424 read_array_index
== input_array_size
))
427 if (Filter::FILTER_OK
== status
)
430 // Write needed data into the filter chain.
431 ASSERT_EQ(Filter::FILTER_NEED_MORE_DATA
, status
);
432 ASSERT_NE(0, filter1
->stream_buffer_size());
433 size_t amount_to_copy
= std::min(
434 static_cast<size_t>(filter1
->stream_buffer_size()),
435 input_array_size
- read_array_index
);
436 memcpy(filter1
->stream_buffer()->data(),
437 input_array
+ read_array_index
,
439 filter1
->FlushStreamBuffer(amount_to_copy
);
440 read_array_index
+= amount_to_copy
;
443 EXPECT_EQ(read_array_index
, input_array_size
);
444 EXPECT_EQ(compare_array_index
, input_array_size
);