OLD | NEW |
---|---|
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 // The basic usage of the Filter interface is described in the comment at | 5 // The basic usage of the Filter interface is described in the comment at |
6 // the beginning of filter.h. If Filter::Factory is passed a vector of | 6 // the beginning of filter.h. If Filter::Factory is passed a vector of |
7 // size greater than 1, that interface is implemented by a series of filters | 7 // size greater than 1, that interface is implemented by a series of filters |
8 // connected in a chain. In such a case the first filter | 8 // connected in a chain. In such a case the first filter |
9 // in the chain proxies calls to ReadData() so that its return values | 9 // in the chain proxies calls to ReadData() so that its return values |
10 // apply to the entire chain. | 10 // apply to the entire chain. |
11 // | 11 // |
12 // In a filter chain, the data flows from first filter (held by the | 12 // In a filter chain, the data flows from first filter (held by the |
13 // caller) down the chain. When ReadData() is called on any filter | 13 // caller) down the chain. When ReadData() is called on any filter |
14 // except for the last filter, it proxies the call down the chain, | 14 // except for the last filter, it proxies the call down the chain, |
15 // filling in the input buffers of subsequent filters if needed (== | 15 // filling in the input buffers of subsequent filters if needed (== |
16 // that filter's last_status() value is FILTER_NEED_MORE_DATA) and | 16 // that filter's last_status() value is FILTER_NEED_MORE_DATA) and |
17 // available (== the current filter has data it can output). The last | 17 // available (== the current filter has data it can output). The last |
18 // Filter will then output data if possible, and return | 18 // Filter will then output data if possible, and return |
19 // FILTER_NEED_MORE_DATA if not. Because the indirection pushes | 19 // FILTER_NEED_MORE_DATA if not. Because the indirection pushes |
20 // data along the filter chain at each level if it's available and the | 20 // data along the filter chain at each level if it's available and the |
21 // next filter needs it, a return value of FILTER_NEED_MORE_DATA from the | 21 // next filter needs it, a return value of FILTER_NEED_MORE_DATA from the |
22 // final filter will apply to the entire chain. | 22 // final filter will apply to the entire chain. |
23 | 23 |
24 #include "net/filter/filter.h" | 24 #include "net/filter/filter.h" |
25 | 25 |
26 #include "base/files/file_path.h" | 26 #include "base/files/file_path.h" |
27 #include "base/metrics/histogram_macros.h" | |
28 #include "base/strings/string_util.h" | 27 #include "base/strings/string_util.h" |
29 #include "net/base/filename_util_unsafe.h" | |
30 #include "net/base/io_buffer.h" | 28 #include "net/base/io_buffer.h" |
31 #include "net/base/mime_util.h" | |
32 #include "net/base/sdch_net_log_params.h" | 29 #include "net/base/sdch_net_log_params.h" |
33 #include "net/filter/gzip_filter.h" | 30 #include "net/filter/gzip_filter.h" |
34 #include "net/filter/sdch_filter.h" | 31 #include "net/filter/sdch_filter.h" |
35 #include "net/url_request/url_request_context.h" | 32 #include "net/url_request/url_request_context.h" |
36 #include "url/gurl.h" | 33 #include "url/gurl.h" |
37 | 34 |
38 namespace net { | 35 namespace net { |
39 | 36 |
40 namespace { | 37 namespace { |
41 | 38 |
42 // Filter types (using canonical lower case only): | 39 // Filter types (using canonical lower case only): |
43 const char kDeflate[] = "deflate"; | 40 const char kDeflate[] = "deflate"; |
44 const char kGZip[] = "gzip"; | 41 const char kGZip[] = "gzip"; |
45 const char kXGZip[] = "x-gzip"; | 42 const char kXGZip[] = "x-gzip"; |
46 const char kSdch[] = "sdch"; | 43 const char kSdch[] = "sdch"; |
47 // compress and x-compress are currently not supported. If we decide to support | 44 // compress and x-compress are currently not supported. If we decide to support |
48 // them, we'll need the same mime type compatibility hack we have for gzip. For | 45 // them, we'll need the same mime type compatibility hack we have for gzip. For |
49 // more information, see Firefox's nsHttpChannel::ProcessNormal. | 46 // more information, see Firefox's nsHttpChannel::ProcessNormal. |
50 | 47 |
51 // Mime types: | 48 // Mime types: |
52 const char kApplicationXGzip[] = "application/x-gzip"; | |
53 const char kApplicationGzip[] = "application/gzip"; | |
54 const char kApplicationXGunzip[] = "application/x-gunzip"; | |
55 const char kTextHtml[] = "text/html"; | 49 const char kTextHtml[] = "text/html"; |
56 | 50 |
57 // Buffer size allocated when de-compressing data. | 51 // Buffer size allocated when de-compressing data. |
58 const int kFilterBufSize = 32 * 1024; | 52 const int kFilterBufSize = 32 * 1024; |
59 | 53 |
60 void LogSdchProblem(const FilterContext& filter_context, | 54 void LogSdchProblem(const FilterContext& filter_context, |
61 SdchProblemCode problem) { | 55 SdchProblemCode problem) { |
62 SdchManager::SdchErrorRecovery(problem); | 56 SdchManager::SdchErrorRecovery(problem); |
63 filter_context.GetNetLog().AddEvent( | 57 filter_context.GetNetLog().AddEvent( |
64 NetLog::TYPE_SDCH_DECODING_ERROR, | 58 NetLog::TYPE_SDCH_DECODING_ERROR, |
(...skipping 132 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
197 } else if (LowerCaseEqualsASCII(filter_type, kSdch)) { | 191 } else if (LowerCaseEqualsASCII(filter_type, kSdch)) { |
198 type_id = FILTER_TYPE_SDCH; | 192 type_id = FILTER_TYPE_SDCH; |
199 } else { | 193 } else { |
200 // Note we also consider "identity" and "uncompressed" UNSUPPORTED as | 194 // Note we also consider "identity" and "uncompressed" UNSUPPORTED as |
201 // filter should be disabled in such cases. | 195 // filter should be disabled in such cases. |
202 type_id = FILTER_TYPE_UNSUPPORTED; | 196 type_id = FILTER_TYPE_UNSUPPORTED; |
203 } | 197 } |
204 return type_id; | 198 return type_id; |
205 } | 199 } |
206 | 200 |
207 namespace { | |
208 | |
209 // Result of running FixupEncodingTypes with a Content-Encoding of gzip. This | |
210 // enum is used for UMA and is available in histograms.xml as | |
211 // GzipEncodingFixupResult. | |
212 enum GzipEncodingFixupResult { | |
213 GZIP_ENCODING_LEFT_AS_IS = 0, | |
214 | |
215 // Cleared because a resource with a GZIP MIME type was being transferred with | |
216 // a Content-Encoding of gzip. | |
217 GZIP_ENCODING_CLEARED_DUE_TO_GZIP_MIME_TYPE = 1, | |
218 | |
219 // Cleared because the resource is known to be a download and the resulting | |
220 // file had a filename that indicating that the contents are GZIP. | |
221 GZIP_ENCODING_CLEARED_DUE_TO_DOWNLOAD_OF_GZIP_FILE = 2, | |
222 | |
223 // Cleared because the resource is not a handled MIME type (and hence will be | |
224 // downloaded), and the predicted filename is one that indicates the contents | |
225 // to be GZIP. | |
226 GZIP_ENCODING_CLEARED_DUE_TO_UNHANDLED_GZIP_NAME = 3, | |
227 | |
228 GZIP_ENCODING_FIXUP_RESULT_MAX_ENTRIES | |
229 }; | |
230 | |
231 } // namespace | |
232 | |
233 // static | 201 // static |
234 void Filter::FixupEncodingTypes( | 202 void Filter::FixupEncodingTypes( |
235 const FilterContext& filter_context, | 203 const FilterContext& filter_context, |
236 std::vector<FilterType>* encoding_types) { | 204 std::vector<FilterType>* encoding_types) { |
237 std::string mime_type; | 205 std::string mime_type; |
238 bool success = filter_context.GetMimeType(&mime_type); | 206 bool success = filter_context.GetMimeType(&mime_type); |
239 DCHECK(success || mime_type.empty()); | 207 DCHECK(success || mime_type.empty()); |
240 | 208 |
241 if ((1 == encoding_types->size()) && | 209 // Historical note: Once upon a time this method used to clear the encoding |
242 (FILTER_TYPE_GZIP == encoding_types->front())) { | 210 // types list if it only contained FILTER_TYPE_GZIP and the response was |
243 GzipEncodingFixupResult fixup_result = GZIP_ENCODING_LEFT_AS_IS; | 211 // likely to be downloaded as a file with gzipped contents. In this case, the |
244 if (LowerCaseEqualsASCII(mime_type, kApplicationXGzip) || | 212 // Content-Encoding is incorrect since there was no intention for the content |
245 LowerCaseEqualsASCII(mime_type, kApplicationGzip) || | 213 // to be decompressed. UMA indicated that the number of servers that exhibit |
246 LowerCaseEqualsASCII(mime_type, kApplicationXGunzip)) { | 214 // this behavior is small, and hence we no longer ignore Content-Encoding for |
247 // The server has told us that it sent us gziped content with a gzip | 215 // downloads. |
Ryan Sleevi
2015/03/19 02:55:06
I'm inclined to suggest nuking this whole bit.
I
Randy Smith (Not in Mondays)
2015/03/19 15:01:06
+1 :-}.
asanka
2015/03/19 20:27:52
Cool. I removed the comment.
I thought I'd add th
| |
248 // content encoding. Sadly, Apache mistakenly sets these headers for all | |
249 // .gz files. We match Firefox's nsHttpChannel::ProcessNormal and ignore | |
250 // the Content-Encoding here. | |
251 encoding_types->clear(); | |
252 fixup_result = GZIP_ENCODING_CLEARED_DUE_TO_GZIP_MIME_TYPE; | |
253 } | |
254 | |
255 GURL url; | |
256 std::string disposition; | |
257 success = filter_context.GetURL(&url); | |
258 DCHECK(success); | |
259 filter_context.GetContentDisposition(&disposition); | |
260 // Don't supply a MIME type here, since that may cause disk IO. | |
261 base::FilePath::StringType extension = | |
262 GenerateFileExtensionUnsafe(url, disposition, "UTF-8", "", "", ""); | |
263 | |
264 if (filter_context.IsDownload()) { | |
265 // We don't want to decompress gzipped files when the user explicitly | |
266 // asks to download them. | |
267 // For the case of svgz files, we use the extension to distinguish | |
268 // between svgz files and svg files compressed with gzip by the server. | |
269 // When viewing a .svgz file, we need to uncompress it, but we don't | |
270 // want to do that when downloading. | |
271 // See Firefox's nonDecodableExtensions in nsExternalHelperAppService.cpp | |
272 if (EndsWith(extension, FILE_PATH_LITERAL(".gz"), false) || | |
273 LowerCaseEqualsASCII(extension, ".tgz") || | |
274 LowerCaseEqualsASCII(extension, ".svgz")) { | |
275 encoding_types->clear(); | |
276 fixup_result = GZIP_ENCODING_CLEARED_DUE_TO_DOWNLOAD_OF_GZIP_FILE; | |
277 } | |
278 } else { | |
279 // When the user does not explicitly ask to download a file, if we get a | |
280 // supported mime type, then we attempt to decompress in order to view it. | |
281 // However, if it's not a supported mime type, then we will attempt to | |
282 // download it, and in that case, don't decompress .gz/.tgz files. | |
283 if ((EndsWith(extension, FILE_PATH_LITERAL(".gz"), false) || | |
284 LowerCaseEqualsASCII(extension, ".tgz")) && | |
285 !IsSupportedMimeType(mime_type)) { | |
286 encoding_types->clear(); | |
287 fixup_result = GZIP_ENCODING_CLEARED_DUE_TO_UNHANDLED_GZIP_NAME; | |
288 } | |
289 } | |
290 | |
291 UMA_HISTOGRAM_ENUMERATION("Net.GzipEncodingFixupResult", fixup_result, | |
292 GZIP_ENCODING_FIXUP_RESULT_MAX_ENTRIES); | |
293 } | |
294 | 216 |
295 // If the request was for SDCH content, then we might need additional fixups. | 217 // If the request was for SDCH content, then we might need additional fixups. |
296 if (!filter_context.SdchDictionariesAdvertised()) { | 218 if (!filter_context.SdchDictionariesAdvertised()) { |
297 // It was not an SDCH request, so we'll just record stats. | 219 // It was not an SDCH request, so we'll just record stats. |
298 if (1 < encoding_types->size()) { | 220 if (1 < encoding_types->size()) { |
299 // Multiple filters were intended to only be used for SDCH (thus far!) | 221 // Multiple filters were intended to only be used for SDCH (thus far!) |
300 LogSdchProblem(filter_context, SDCH_MULTIENCODING_FOR_NON_SDCH_REQUEST); | 222 LogSdchProblem(filter_context, SDCH_MULTIENCODING_FOR_NON_SDCH_REQUEST); |
301 } | 223 } |
302 if ((1 == encoding_types->size()) && | 224 if ((1 == encoding_types->size()) && |
303 (FILTER_TYPE_SDCH == encoding_types->front())) { | 225 (FILTER_TYPE_SDCH == encoding_types->front())) { |
(...skipping 185 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
489 | 411 |
490 void Filter::PushDataIntoNextFilter() { | 412 void Filter::PushDataIntoNextFilter() { |
491 IOBuffer* next_buffer = next_filter_->stream_buffer(); | 413 IOBuffer* next_buffer = next_filter_->stream_buffer(); |
492 int next_size = next_filter_->stream_buffer_size(); | 414 int next_size = next_filter_->stream_buffer_size(); |
493 last_status_ = ReadFilteredData(next_buffer->data(), &next_size); | 415 last_status_ = ReadFilteredData(next_buffer->data(), &next_size); |
494 if (FILTER_ERROR != last_status_) | 416 if (FILTER_ERROR != last_status_) |
495 next_filter_->FlushStreamBuffer(next_size); | 417 next_filter_->FlushStreamBuffer(next_size); |
496 } | 418 } |
497 | 419 |
498 } // namespace net | 420 } // namespace net |
OLD | NEW |