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 |