| 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 #include "net/filter/filter.h" | 5 #include "net/filter/filter.h" |
| 6 | 6 |
| 7 #include "base/files/file_path.h" | 7 #include "base/files/file_path.h" |
| 8 #include "base/strings/string_util.h" | 8 #include "base/strings/string_util.h" |
| 9 #include "net/base/filename_util.h" | 9 #include "net/base/filename_util.h" |
| 10 #include "net/base/io_buffer.h" | 10 #include "net/base/io_buffer.h" |
| 11 #include "net/base/mime_util.h" | 11 #include "net/base/mime_util.h" |
| 12 #include "net/filter/gzip_filter.h" | 12 #include "net/filter/gzip_filter.h" |
| 13 #include "net/filter/sdch_filter.h" | 13 #include "net/filter/sdch_filter.h" |
| 14 #include "url/gurl.h" | 14 #include "url/gurl.h" |
| 15 | 15 |
| 16 namespace { | 16 namespace { |
| 17 | 17 |
| 18 // Filter types (using canonical lower case only): | 18 // Filter types (using canonical lower case only): |
| 19 const char kDeflate[] = "deflate"; | 19 const char kDeflate[] = "deflate"; |
| 20 const char kGZip[] = "gzip"; | 20 const char kGZip[] = "gzip"; |
| 21 const char kXGZip[] = "x-gzip"; | 21 const char kXGZip[] = "x-gzip"; |
| 22 const char kSdch[] = "sdch"; | 22 const char kSdch[] = "sdch"; |
| 23 // compress and x-compress are currently not supported. If we decide to support | 23 // compress and x-compress are currently not supported. If we decide to support |
| 24 // them, we'll need the same mime type compatibility hack we have for gzip. For | 24 // them, we'll need the same mime type compatibility hack we have for gzip. For |
| 25 // more information, see Firefox's nsHttpChannel::ProcessNormal. | 25 // more information, see Firefox's nsHttpChannel::ProcessNormal. |
| 26 | 26 |
| 27 // Mime types: | 27 // Mime types: |
| 28 const char kApplicationXGzip[] = "application/x-gzip"; | 28 const char kApplicationXGzip[] = "application/x-gzip"; |
| 29 const char kApplicationGzip[] = "application/gzip"; | 29 const char kApplicationGzip[] = "application/gzip"; |
| 30 const char kApplicationXGunzip[] = "application/x-gunzip"; | 30 const char kApplicationXGunzip[] = "application/x-gunzip"; |
| 31 const char kTextHtml[] = "text/html"; | 31 const char kTextHtml[] = "text/html"; |
| 32 | 32 |
| 33 // Buffer size allocated when de-compressing data. | 33 // Buffer size allocated when de-compressing data. |
| 34 const int kFilterBufSize = 32 * 1024; | 34 const int kFilterBufSize = 32 * 1024; |
| 35 | 35 |
| 36 } // namespace | 36 } // namespace |
| 37 | 37 |
| 38 namespace net { | 38 namespace net { |
| 39 | 39 |
| 40 FilterContext::~FilterContext() { | 40 FilterContext::~FilterContext() { |
| 41 } | 41 } |
| 42 | 42 |
| 43 Filter::~Filter() {} | 43 Filter::~Filter() { |
| 44 } |
| 44 | 45 |
| 45 // static | 46 // static |
| 46 Filter* Filter::Factory(const std::vector<FilterType>& filter_types, | 47 Filter* Filter::Factory(const std::vector<FilterType>& filter_types, |
| 47 const FilterContext& filter_context) { | 48 const FilterContext& filter_context) { |
| 48 if (filter_types.empty()) | 49 if (filter_types.empty()) |
| 49 return NULL; | 50 return NULL; |
| 50 | 51 |
| 51 Filter* filter_list = NULL; // Linked list of filters. | 52 Filter* filter_list = NULL; // Linked list of filters. |
| 52 for (size_t i = 0; i < filter_types.size(); i++) { | 53 for (size_t i = 0; i < filter_types.size(); i++) { |
| 53 filter_list = PrependNewFilter(filter_types[i], filter_context, | 54 filter_list = PrependNewFilter( |
| 54 kFilterBufSize, filter_list); | 55 filter_types[i], filter_context, kFilterBufSize, filter_list); |
| 55 if (!filter_list) | 56 if (!filter_list) |
| 56 return NULL; | 57 return NULL; |
| 57 } | 58 } |
| 58 return filter_list; | 59 return filter_list; |
| 59 } | 60 } |
| 60 | 61 |
| 61 // static | 62 // static |
| 62 Filter* Filter::GZipFactory() { | 63 Filter* Filter::GZipFactory() { |
| 63 return InitGZipFilter(FILTER_TYPE_GZIP, kFilterBufSize); | 64 return InitGZipFilter(FILTER_TYPE_GZIP, kFilterBufSize); |
| 64 } | 65 } |
| 65 | 66 |
| 66 // static | 67 // static |
| 67 Filter* Filter::FactoryForTests(const std::vector<FilterType>& filter_types, | 68 Filter* Filter::FactoryForTests(const std::vector<FilterType>& filter_types, |
| 68 const FilterContext& filter_context, | 69 const FilterContext& filter_context, |
| 69 int buffer_size) { | 70 int buffer_size) { |
| 70 if (filter_types.empty()) | 71 if (filter_types.empty()) |
| 71 return NULL; | 72 return NULL; |
| 72 | 73 |
| 73 Filter* filter_list = NULL; // Linked list of filters. | 74 Filter* filter_list = NULL; // Linked list of filters. |
| 74 for (size_t i = 0; i < filter_types.size(); i++) { | 75 for (size_t i = 0; i < filter_types.size(); i++) { |
| 75 filter_list = PrependNewFilter(filter_types[i], filter_context, | 76 filter_list = PrependNewFilter( |
| 76 buffer_size, filter_list); | 77 filter_types[i], filter_context, buffer_size, filter_list); |
| 77 if (!filter_list) | 78 if (!filter_list) |
| 78 return NULL; | 79 return NULL; |
| 79 } | 80 } |
| 80 return filter_list; | 81 return filter_list; |
| 81 } | 82 } |
| 82 | 83 |
| 83 Filter::FilterStatus Filter::ReadData(char* dest_buffer, int* dest_len) { | 84 Filter::FilterStatus Filter::ReadData(char* dest_buffer, int* dest_len) { |
| 84 const int dest_buffer_capacity = *dest_len; | 85 const int dest_buffer_capacity = *dest_len; |
| 85 if (last_status_ == FILTER_ERROR) | 86 if (last_status_ == FILTER_ERROR) |
| 86 return last_status_; | 87 return last_status_; |
| 87 if (!next_filter_.get()) | 88 if (!next_filter_.get()) |
| 88 return last_status_ = ReadFilteredData(dest_buffer, dest_len); | 89 return last_status_ = ReadFilteredData(dest_buffer, dest_len); |
| 89 if (last_status_ == FILTER_NEED_MORE_DATA && !stream_data_len()) | 90 if (last_status_ == FILTER_NEED_MORE_DATA && !stream_data_len()) |
| 90 return next_filter_->ReadData(dest_buffer, dest_len); | 91 return next_filter_->ReadData(dest_buffer, dest_len); |
| 91 | 92 |
| 92 do { | 93 do { |
| 93 if (next_filter_->last_status() == FILTER_NEED_MORE_DATA) { | 94 if (next_filter_->last_status() == FILTER_NEED_MORE_DATA) { |
| 94 PushDataIntoNextFilter(); | 95 PushDataIntoNextFilter(); |
| 95 if (FILTER_ERROR == last_status_) | 96 if (FILTER_ERROR == last_status_) |
| 96 return FILTER_ERROR; | 97 return FILTER_ERROR; |
| 97 } | 98 } |
| 98 *dest_len = dest_buffer_capacity; // Reset the input/output parameter. | 99 *dest_len = dest_buffer_capacity; // Reset the input/output parameter. |
| 99 next_filter_->ReadData(dest_buffer, dest_len); | 100 next_filter_->ReadData(dest_buffer, dest_len); |
| 100 if (FILTER_NEED_MORE_DATA == last_status_) | 101 if (FILTER_NEED_MORE_DATA == last_status_) |
| 101 return next_filter_->last_status(); | 102 return next_filter_->last_status(); |
| 102 | 103 |
| 103 // In the case where this filter has data internally, and is indicating such | 104 // In the case where this filter has data internally, and is indicating such |
| 104 // with a last_status_ of FILTER_OK, but at the same time the next filter in | 105 // with a last_status_ of FILTER_OK, but at the same time the next filter in |
| 105 // the chain indicated it FILTER_NEED_MORE_DATA, we have to be cautious | 106 // the chain indicated it FILTER_NEED_MORE_DATA, we have to be cautious |
| 106 // about confusing the caller. The API confusion can appear if we return | 107 // about confusing the caller. The API confusion can appear if we return |
| 107 // FILTER_OK (suggesting we have more data in aggregate), but yet we don't | 108 // FILTER_OK (suggesting we have more data in aggregate), but yet we don't |
| 108 // populate our output buffer. When that is the case, we need to | 109 // populate our output buffer. When that is the case, we need to |
| 109 // alternately call our filter element, and the next_filter element until we | 110 // alternately call our filter element, and the next_filter element until we |
| 110 // get out of this state (by pumping data into the next filter until it | 111 // get out of this state (by pumping data into the next filter until it |
| 111 // outputs data, or it runs out of data and reports that it NEED_MORE_DATA.) | 112 // outputs data, or it runs out of data and reports that it NEED_MORE_DATA.) |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 146 type_id = FILTER_TYPE_SDCH; | 147 type_id = FILTER_TYPE_SDCH; |
| 147 } else { | 148 } else { |
| 148 // Note we also consider "identity" and "uncompressed" UNSUPPORTED as | 149 // Note we also consider "identity" and "uncompressed" UNSUPPORTED as |
| 149 // filter should be disabled in such cases. | 150 // filter should be disabled in such cases. |
| 150 type_id = FILTER_TYPE_UNSUPPORTED; | 151 type_id = FILTER_TYPE_UNSUPPORTED; |
| 151 } | 152 } |
| 152 return type_id; | 153 return type_id; |
| 153 } | 154 } |
| 154 | 155 |
| 155 // static | 156 // static |
| 156 void Filter::FixupEncodingTypes( | 157 void Filter::FixupEncodingTypes(const FilterContext& filter_context, |
| 157 const FilterContext& filter_context, | 158 std::vector<FilterType>* encoding_types) { |
| 158 std::vector<FilterType>* encoding_types) { | |
| 159 std::string mime_type; | 159 std::string mime_type; |
| 160 bool success = filter_context.GetMimeType(&mime_type); | 160 bool success = filter_context.GetMimeType(&mime_type); |
| 161 DCHECK(success || mime_type.empty()); | 161 DCHECK(success || mime_type.empty()); |
| 162 | 162 |
| 163 if ((1 == encoding_types->size()) && | 163 if ((1 == encoding_types->size()) && |
| 164 (FILTER_TYPE_GZIP == encoding_types->front())) { | 164 (FILTER_TYPE_GZIP == encoding_types->front())) { |
| 165 if (LowerCaseEqualsASCII(mime_type, kApplicationXGzip) || | 165 if (LowerCaseEqualsASCII(mime_type, kApplicationXGzip) || |
| 166 LowerCaseEqualsASCII(mime_type, kApplicationGzip) || | 166 LowerCaseEqualsASCII(mime_type, kApplicationGzip) || |
| 167 LowerCaseEqualsASCII(mime_type, kApplicationXGunzip)) | 167 LowerCaseEqualsASCII(mime_type, kApplicationXGunzip)) |
| 168 // The server has told us that it sent us gziped content with a gzip | 168 // The server has told us that it sent us gziped content with a gzip |
| 169 // content encoding. Sadly, Apache mistakenly sets these headers for all | 169 // content encoding. Sadly, Apache mistakenly sets these headers for all |
| 170 // .gz files. We match Firefox's nsHttpChannel::ProcessNormal and ignore | 170 // .gz files. We match Firefox's nsHttpChannel::ProcessNormal and ignore |
| 171 // the Content-Encoding here. | 171 // the Content-Encoding here. |
| 172 encoding_types->clear(); | 172 encoding_types->clear(); |
| 173 | 173 |
| 174 GURL url; | 174 GURL url; |
| 175 std::string disposition; | 175 std::string disposition; |
| 176 success = filter_context.GetURL(&url); | 176 success = filter_context.GetURL(&url); |
| 177 DCHECK(success); | 177 DCHECK(success); |
| 178 filter_context.GetContentDisposition(&disposition); | 178 filter_context.GetContentDisposition(&disposition); |
| 179 // Don't supply a MIME type here, since that may cause disk IO. | 179 // Don't supply a MIME type here, since that may cause disk IO. |
| 180 base::FilePath filepath = GenerateFileName(url, disposition, "UTF-8", "", | 180 base::FilePath filepath = |
| 181 "", ""); | 181 GenerateFileName(url, disposition, "UTF-8", "", "", ""); |
| 182 base::FilePath::StringType extension = filepath.Extension(); | 182 base::FilePath::StringType extension = filepath.Extension(); |
| 183 | 183 |
| 184 if (filter_context.IsDownload()) { | 184 if (filter_context.IsDownload()) { |
| 185 // We don't want to decompress gzipped files when the user explicitly | 185 // We don't want to decompress gzipped files when the user explicitly |
| 186 // asks to download them. | 186 // asks to download them. |
| 187 // For the case of svgz files, we use the extension to distinguish | 187 // For the case of svgz files, we use the extension to distinguish |
| 188 // between svgz files and svg files compressed with gzip by the server. | 188 // between svgz files and svg files compressed with gzip by the server. |
| 189 // When viewing a .svgz file, we need to uncompress it, but we don't | 189 // When viewing a .svgz file, we need to uncompress it, but we don't |
| 190 // want to do that when downloading. | 190 // want to do that when downloading. |
| 191 // See Firefox's nonDecodableExtensions in nsExternalHelperAppService.cpp | 191 // See Firefox's nonDecodableExtensions in nsExternalHelperAppService.cpp |
| (...skipping 16 matching lines...) Expand all Loading... |
| 208 // If the request was for SDCH content, then we might need additional fixups. | 208 // If the request was for SDCH content, then we might need additional fixups. |
| 209 if (!filter_context.IsSdchResponse()) { | 209 if (!filter_context.IsSdchResponse()) { |
| 210 // It was not an SDCH request, so we'll just record stats. | 210 // It was not an SDCH request, so we'll just record stats. |
| 211 if (1 < encoding_types->size()) { | 211 if (1 < encoding_types->size()) { |
| 212 // Multiple filters were intended to only be used for SDCH (thus far!) | 212 // Multiple filters were intended to only be used for SDCH (thus far!) |
| 213 SdchManager::SdchErrorRecovery( | 213 SdchManager::SdchErrorRecovery( |
| 214 SdchManager::MULTIENCODING_FOR_NON_SDCH_REQUEST); | 214 SdchManager::MULTIENCODING_FOR_NON_SDCH_REQUEST); |
| 215 } | 215 } |
| 216 if ((1 == encoding_types->size()) && | 216 if ((1 == encoding_types->size()) && |
| 217 (FILTER_TYPE_SDCH == encoding_types->front())) { | 217 (FILTER_TYPE_SDCH == encoding_types->front())) { |
| 218 SdchManager::SdchErrorRecovery( | 218 SdchManager::SdchErrorRecovery( |
| 219 SdchManager::SDCH_CONTENT_ENCODE_FOR_NON_SDCH_REQUEST); | 219 SdchManager::SDCH_CONTENT_ENCODE_FOR_NON_SDCH_REQUEST); |
| 220 } | 220 } |
| 221 return; | 221 return; |
| 222 } | 222 } |
| 223 | 223 |
| 224 // The request was tagged as an SDCH request, which means the server supplied | 224 // The request was tagged as an SDCH request, which means the server supplied |
| 225 // a dictionary, and we advertised it in the request. Some proxies will do | 225 // a dictionary, and we advertised it in the request. Some proxies will do |
| 226 // very strange things to the request, or the response, so we have to handle | 226 // very strange things to the request, or the response, so we have to handle |
| 227 // them gracefully. | 227 // them gracefully. |
| 228 | 228 |
| 229 // If content encoding included SDCH, then everything is "relatively" fine. | 229 // If content encoding included SDCH, then everything is "relatively" fine. |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 264 // The one unresolved failure mode comes when we advertise a dictionary, and | 264 // The one unresolved failure mode comes when we advertise a dictionary, and |
| 265 // the server tries to *send* a gzipped file (not gzip encode content), and | 265 // the server tries to *send* a gzipped file (not gzip encode content), and |
| 266 // then we could do a gzip decode :-(. Since SDCH is only (currently) | 266 // then we could do a gzip decode :-(. Since SDCH is only (currently) |
| 267 // supported server side on paths that only send HTML content, this mode has | 267 // supported server side on paths that only send HTML content, this mode has |
| 268 // never surfaced in the wild (and is unlikely to). | 268 // never surfaced in the wild (and is unlikely to). |
| 269 // We will gather a lot of stats as we perform the fixups | 269 // We will gather a lot of stats as we perform the fixups |
| 270 if (StartsWithASCII(mime_type, kTextHtml, false)) { | 270 if (StartsWithASCII(mime_type, kTextHtml, false)) { |
| 271 // Suspicious case: Advertised dictionary, but server didn't use sdch, and | 271 // Suspicious case: Advertised dictionary, but server didn't use sdch, and |
| 272 // we're HTML tagged. | 272 // we're HTML tagged. |
| 273 if (encoding_types->empty()) { | 273 if (encoding_types->empty()) { |
| 274 SdchManager::SdchErrorRecovery( | 274 SdchManager::SdchErrorRecovery(SdchManager::ADDED_CONTENT_ENCODING); |
| 275 SdchManager::ADDED_CONTENT_ENCODING); | |
| 276 } else if (1 == encoding_types->size()) { | 275 } else if (1 == encoding_types->size()) { |
| 277 SdchManager::SdchErrorRecovery( | 276 SdchManager::SdchErrorRecovery(SdchManager::FIXED_CONTENT_ENCODING); |
| 278 SdchManager::FIXED_CONTENT_ENCODING); | |
| 279 } else { | 277 } else { |
| 280 SdchManager::SdchErrorRecovery( | 278 SdchManager::SdchErrorRecovery(SdchManager::FIXED_CONTENT_ENCODINGS); |
| 281 SdchManager::FIXED_CONTENT_ENCODINGS); | |
| 282 } | 279 } |
| 283 } else { | 280 } else { |
| 284 // Remarkable case!?! We advertised an SDCH dictionary, content-encoding | 281 // Remarkable case!?! We advertised an SDCH dictionary, content-encoding |
| 285 // was not marked for SDCH processing: Why did the server suggest an SDCH | 282 // was not marked for SDCH processing: Why did the server suggest an SDCH |
| 286 // dictionary in the first place??. Also, the content isn't | 283 // dictionary in the first place??. Also, the content isn't |
| 287 // tagged as HTML, despite the fact that SDCH encoding is mostly likely for | 284 // tagged as HTML, despite the fact that SDCH encoding is mostly likely for |
| 288 // HTML: Did some anti-virus system strip this tag (sometimes they strip | 285 // HTML: Did some anti-virus system strip this tag (sometimes they strip |
| 289 // accept-encoding headers on the request)?? Does the content encoding not | 286 // accept-encoding headers on the request)?? Does the content encoding not |
| 290 // start with "text/html" for some other reason?? We'll report this as a | 287 // start with "text/html" for some other reason?? We'll report this as a |
| 291 // fixup to a binary file, but it probably really is text/html (some how). | 288 // fixup to a binary file, but it probably really is text/html (some how). |
| (...skipping 22 matching lines...) Expand all Loading... |
| 314 FILTER_TYPE_GZIP_HELPING_SDCH); | 311 FILTER_TYPE_GZIP_HELPING_SDCH); |
| 315 encoding_types->insert(encoding_types->begin(), FILTER_TYPE_SDCH_POSSIBLE); | 312 encoding_types->insert(encoding_types->begin(), FILTER_TYPE_SDCH_POSSIBLE); |
| 316 return; | 313 return; |
| 317 } | 314 } |
| 318 | 315 |
| 319 Filter::Filter() | 316 Filter::Filter() |
| 320 : stream_buffer_(NULL), | 317 : stream_buffer_(NULL), |
| 321 stream_buffer_size_(0), | 318 stream_buffer_size_(0), |
| 322 next_stream_data_(NULL), | 319 next_stream_data_(NULL), |
| 323 stream_data_len_(0), | 320 stream_data_len_(0), |
| 324 last_status_(FILTER_NEED_MORE_DATA) {} | 321 last_status_(FILTER_NEED_MORE_DATA) { |
| 322 } |
| 325 | 323 |
| 326 Filter::FilterStatus Filter::CopyOut(char* dest_buffer, int* dest_len) { | 324 Filter::FilterStatus Filter::CopyOut(char* dest_buffer, int* dest_len) { |
| 327 int out_len; | 325 int out_len; |
| 328 int input_len = *dest_len; | 326 int input_len = *dest_len; |
| 329 *dest_len = 0; | 327 *dest_len = 0; |
| 330 | 328 |
| 331 if (0 == stream_data_len_) | 329 if (0 == stream_data_len_) |
| 332 return Filter::FILTER_NEED_MORE_DATA; | 330 return Filter::FILTER_NEED_MORE_DATA; |
| 333 | 331 |
| 334 out_len = std::min(input_len, stream_data_len_); | 332 out_len = std::min(input_len, stream_data_len_); |
| (...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 399 | 397 |
| 400 void Filter::PushDataIntoNextFilter() { | 398 void Filter::PushDataIntoNextFilter() { |
| 401 IOBuffer* next_buffer = next_filter_->stream_buffer(); | 399 IOBuffer* next_buffer = next_filter_->stream_buffer(); |
| 402 int next_size = next_filter_->stream_buffer_size(); | 400 int next_size = next_filter_->stream_buffer_size(); |
| 403 last_status_ = ReadFilteredData(next_buffer->data(), &next_size); | 401 last_status_ = ReadFilteredData(next_buffer->data(), &next_size); |
| 404 if (FILTER_ERROR != last_status_) | 402 if (FILTER_ERROR != last_status_) |
| 405 next_filter_->FlushStreamBuffer(next_size); | 403 next_filter_->FlushStreamBuffer(next_size); |
| 406 } | 404 } |
| 407 | 405 |
| 408 } // namespace net | 406 } // namespace net |
| OLD | NEW |