Chromium Code Reviews| 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 |