OLD | NEW |
1 // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2006-2008 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/base/filter.h" | 5 #include "net/base/filter.h" |
6 | 6 |
7 #include "base/string_util.h" | 7 #include "base/string_util.h" |
8 #include "net/base/gzip_filter.h" | 8 #include "net/base/gzip_filter.h" |
9 #include "net/base/bzip2_filter.h" | 9 #include "net/base/bzip2_filter.h" |
10 #include "net/base/sdch_filter.h" | 10 #include "net/base/sdch_filter.h" |
11 | 11 |
12 namespace { | 12 namespace { |
13 | 13 |
14 // Filter types: | 14 // Filter types (using canonical lower case only): |
15 const char kDeflate[] = "deflate"; | 15 const char kDeflate[] = "deflate"; |
16 const char kGZip[] = "gzip"; | 16 const char kGZip[] = "gzip"; |
17 const char kXGZip[] = "x-gzip"; | 17 const char kXGZip[] = "x-gzip"; |
18 const char kBZip2[] = "bzip2"; | 18 const char kBZip2[] = "bzip2"; |
19 const char kXBZip2[] = "x-bzip2"; | 19 const char kXBZip2[] = "x-bzip2"; |
20 const char kSdch[] = "sdch"; | 20 const char kSdch[] = "sdch"; |
21 // compress and x-compress are currently not supported. If we decide to support | 21 // compress and x-compress are currently not supported. If we decide to support |
22 // them, we'll need the same mime type compatibility hack we have for gzip. For | 22 // them, we'll need the same mime type compatibility hack we have for gzip. For |
23 // more information, see Firefox's nsHttpChannel::ProcessNormal. | 23 // more information, see Firefox's nsHttpChannel::ProcessNormal. |
24 const char kCompress[] = "compress"; | 24 const char kCompress[] = "compress"; |
25 const char kXCompress[] = "x-compress"; | 25 const char kXCompress[] = "x-compress"; |
26 const char kIdentity[] = "identity"; | 26 const char kIdentity[] = "identity"; |
27 const char kUncompressed[] = "uncompressed"; | 27 const char kUncompressed[] = "uncompressed"; |
28 | 28 |
29 // Mime types: | 29 // Mime types: |
30 const char kApplicationXGzip[] = "application/x-gzip"; | 30 const char kApplicationXGzip[] = "application/x-gzip"; |
31 const char kApplicationGzip[] = "application/gzip"; | 31 const char kApplicationGzip[] = "application/gzip"; |
32 const char kApplicationXGunzip[] = "application/x-gunzip"; | 32 const char kApplicationXGunzip[] = "application/x-gunzip"; |
33 const char kApplicationXCompress[] = "application/x-compress"; | 33 const char kApplicationXCompress[] = "application/x-compress"; |
34 const char kApplicationCompress[] = "application/compress"; | 34 const char kApplicationCompress[] = "application/compress"; |
| 35 const char kTextHtml[] = "text/html"; |
35 | 36 |
36 } // namespace | 37 } // namespace |
37 | 38 |
38 Filter* Filter::Factory(const std::vector<std::string>& filter_types, | 39 Filter* Filter::Factory(const std::vector<FilterType>& filter_types, |
39 const std::string& mime_type, | |
40 int buffer_size) { | 40 int buffer_size) { |
41 if (filter_types.empty() || buffer_size < 0) | 41 if (filter_types.empty() || buffer_size < 0) |
42 return NULL; | 42 return NULL; |
43 | 43 |
44 std::string safe_mime_type = (filter_types.size() > 1) ? "" : mime_type; | |
45 Filter* filter_list = NULL; // Linked list of filters. | 44 Filter* filter_list = NULL; // Linked list of filters. |
46 FilterType type_id = FILTER_TYPE_UNSUPPORTED; | |
47 for (size_t i = 0; i < filter_types.size(); i++) { | 45 for (size_t i = 0; i < filter_types.size(); i++) { |
48 type_id = ConvertEncodingToType(filter_types[i], safe_mime_type); | 46 filter_list = PrependNewFilter(filter_types[i], buffer_size, filter_list); |
49 filter_list = PrependNewFilter(type_id, buffer_size, filter_list); | |
50 if (!filter_list) | 47 if (!filter_list) |
51 return NULL; | 48 return NULL; |
52 } | 49 } |
53 | 50 |
54 // Handle proxy that changes content encoding "sdch,gzip" into "sdch". | |
55 if (1 == filter_types.size() && FILTER_TYPE_SDCH == type_id) | |
56 filter_list = PrependNewFilter(FILTER_TYPE_GZIP_HELPING_SDCH, buffer_size, | |
57 filter_list); | |
58 return filter_list; | 51 return filter_list; |
59 } | 52 } |
60 | 53 |
61 // static | 54 // static |
62 Filter::FilterType Filter::ConvertEncodingToType(const std::string& filter_type, | 55 Filter::FilterType Filter::ConvertEncodingToType( |
63 const std::string& mime_type) { | 56 const std::string& filter_type) { |
64 FilterType type_id; | 57 FilterType type_id; |
65 if (LowerCaseEqualsASCII(filter_type, kDeflate)) { | 58 if (LowerCaseEqualsASCII(filter_type, kDeflate)) { |
66 type_id = FILTER_TYPE_DEFLATE; | 59 type_id = FILTER_TYPE_DEFLATE; |
67 } else if (LowerCaseEqualsASCII(filter_type, kGZip) || | 60 } else if (LowerCaseEqualsASCII(filter_type, kGZip) || |
68 LowerCaseEqualsASCII(filter_type, kXGZip)) { | 61 LowerCaseEqualsASCII(filter_type, kXGZip)) { |
69 if (LowerCaseEqualsASCII(mime_type, kApplicationXGzip) || | 62 type_id = FILTER_TYPE_GZIP; |
70 LowerCaseEqualsASCII(mime_type, kApplicationGzip) || | |
71 LowerCaseEqualsASCII(mime_type, kApplicationXGunzip)) { | |
72 // The server has told us that it sent us gziped content with a gzip | |
73 // content encoding. Sadly, Apache mistakenly sets these headers for all | |
74 // .gz files. We match Firefox's nsHttpChannel::ProcessNormal and ignore | |
75 // the Content-Encoding here. | |
76 // TODO(jar): Move all this encoding type "fixup" into the | |
77 // GetContentEncoding() methods. Combine this defaulting with SDCH fixup. | |
78 type_id = FILTER_TYPE_UNSUPPORTED; | |
79 } else { | |
80 type_id = FILTER_TYPE_GZIP; | |
81 } | |
82 } else if (LowerCaseEqualsASCII(filter_type, kBZip2) || | 63 } else if (LowerCaseEqualsASCII(filter_type, kBZip2) || |
83 LowerCaseEqualsASCII(filter_type, kXBZip2)) { | 64 LowerCaseEqualsASCII(filter_type, kXBZip2)) { |
84 type_id = FILTER_TYPE_BZIP2; | 65 type_id = FILTER_TYPE_BZIP2; |
85 } else if (LowerCaseEqualsASCII(filter_type, kSdch)) { | 66 } else if (LowerCaseEqualsASCII(filter_type, kSdch)) { |
86 type_id = FILTER_TYPE_SDCH; | 67 type_id = FILTER_TYPE_SDCH; |
87 } else { | 68 } else { |
88 // Note we also consider "identity" and "uncompressed" UNSUPPORTED as | 69 // Note we also consider "identity" and "uncompressed" UNSUPPORTED as |
89 // filter should be disabled in such cases. | 70 // filter should be disabled in such cases. |
90 type_id = FILTER_TYPE_UNSUPPORTED; | 71 type_id = FILTER_TYPE_UNSUPPORTED; |
91 } | 72 } |
92 return type_id; | 73 return type_id; |
93 } | 74 } |
94 | 75 |
95 // static | 76 // static |
| 77 void Filter::FixupEncodingTypes( |
| 78 bool is_sdch_response, |
| 79 const std::string& mime_type, |
| 80 std::vector<FilterType>* encoding_types) { |
| 81 |
| 82 if ((1 == encoding_types->size()) && |
| 83 (FILTER_TYPE_GZIP == encoding_types->front())) { |
| 84 if (LowerCaseEqualsASCII(mime_type, kApplicationXGzip) || |
| 85 LowerCaseEqualsASCII(mime_type, kApplicationGzip) || |
| 86 LowerCaseEqualsASCII(mime_type, kApplicationXGunzip)) |
| 87 // The server has told us that it sent us gziped content with a gzip |
| 88 // content encoding. Sadly, Apache mistakenly sets these headers for all |
| 89 // .gz files. We match Firefox's nsHttpChannel::ProcessNormal and ignore |
| 90 // the Content-Encoding here. |
| 91 encoding_types->clear(); |
| 92 return; |
| 93 } |
| 94 |
| 95 if (!is_sdch_response) |
| 96 return; |
| 97 |
| 98 // If content encoding included SDCH, then everything is fine. |
| 99 if (!encoding_types->empty() && |
| 100 (FILTER_TYPE_SDCH == encoding_types->front())) { |
| 101 // Some proxies (found currently in Argentina) strip the Content-Encoding |
| 102 // text from "sdch,gzip" to a mere "sdch" without modifying the compressed |
| 103 // payload. To handle this gracefully, we simulate the "probably" deleted |
| 104 // ",gzip" by appending a tentative gzip decode, which will default to a |
| 105 // no-op pass through filter if it doesn't get gzip headers where expected. |
| 106 if (1 == encoding_types->size()) |
| 107 encoding_types->push_back(FILTER_TYPE_GZIP_HELPING_SDCH); |
| 108 return; |
| 109 } |
| 110 |
| 111 // SDCH "search results" protective hack: To make sure we don't break the only |
| 112 // currently deployed SDCH enabled server! Be VERY cautious about proxies that |
| 113 // strip all content-encoding to not include sdch. IF we don't see content |
| 114 // encodings that seem to match what we'd expect from a server that asked us |
| 115 // to use a dictionary (and we advertised said dictionary in the GET), then |
| 116 // we set the encoding to (try to) use SDCH to decode. Note that SDCH will |
| 117 // degrade into a pass-through filter if it doesn't have a viable dictionary |
| 118 // hash in its header. Also note that a solo "sdch" will implicitly create |
| 119 // a "sdch,gzip" decoding filter, where the gzip portion will degrade to a |
| 120 // pass through if a gzip header is not encountered. Hence we can replace |
| 121 // "gzip" with "sdch" and "everything will work." |
| 122 // The one failure mode comes when we advertise a dictionary, and the server |
| 123 // tries to *send* a gzipped file (not gzip encode content), and then we could |
| 124 // do a gzip decode :-(. Since current server support does not ever see such |
| 125 // a transfer, we are safe (for now). |
| 126 if (LowerCaseEqualsASCII(mime_type, kTextHtml)) { |
| 127 // Suspicious case: Advertised dictionary, but server didn't use sdch, even |
| 128 // though it is text_html content. |
| 129 if (encoding_types->empty()) |
| 130 SdchManager::SdchErrorRecovery(SdchManager::ADDED_CONTENT_ENCODING); |
| 131 else if (1 == encoding_types->size()) |
| 132 SdchManager::SdchErrorRecovery(SdchManager::FIXED_CONTENT_ENCODING); |
| 133 else |
| 134 SdchManager::SdchErrorRecovery(SdchManager::FIXED_CONTENT_ENCODINGS); |
| 135 encoding_types->clear(); |
| 136 encoding_types->push_back(FILTER_TYPE_SDCH); |
| 137 encoding_types->push_back(FILTER_TYPE_GZIP_HELPING_SDCH); |
| 138 return; |
| 139 } |
| 140 |
| 141 // It didn't have SDCH encoding... but it wasn't HTML... so maybe it really |
| 142 // wasn't SDCH encoded. It would be nice if we knew this, and didn't bother |
| 143 // to propose a dictionary etc., but current SDCH spec does not provide a nice |
| 144 // way for us to conclude that. Perhaps in the future, this case will be much |
| 145 // more rare. |
| 146 return; |
| 147 } |
| 148 |
| 149 // static |
96 Filter* Filter::PrependNewFilter(FilterType type_id, int buffer_size, | 150 Filter* Filter::PrependNewFilter(FilterType type_id, int buffer_size, |
97 Filter* filter_list) { | 151 Filter* filter_list) { |
98 Filter* first_filter = NULL; // Soon to be start of chain. | 152 Filter* first_filter = NULL; // Soon to be start of chain. |
99 switch (type_id) { | 153 switch (type_id) { |
100 case FILTER_TYPE_GZIP_HELPING_SDCH: | 154 case FILTER_TYPE_GZIP_HELPING_SDCH: |
101 case FILTER_TYPE_DEFLATE: | 155 case FILTER_TYPE_DEFLATE: |
102 case FILTER_TYPE_GZIP: { | 156 case FILTER_TYPE_GZIP: { |
103 scoped_ptr<GZipFilter> gz_filter(new GZipFilter()); | 157 scoped_ptr<GZipFilter> gz_filter(new GZipFilter()); |
104 if (gz_filter->InitBuffer(buffer_size)) { | 158 if (gz_filter->InitBuffer(buffer_size)) { |
105 if (gz_filter->InitDecoding(type_id)) { | 159 if (gz_filter->InitDecoding(type_id)) { |
(...skipping 134 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
240 stream_data_len_ = stream_data_len; | 294 stream_data_len_ = stream_data_len; |
241 return true; | 295 return true; |
242 } | 296 } |
243 | 297 |
244 void Filter::SetURL(const GURL& url) { | 298 void Filter::SetURL(const GURL& url) { |
245 url_ = url; | 299 url_ = url; |
246 if (next_filter_.get()) | 300 if (next_filter_.get()) |
247 next_filter_->SetURL(url); | 301 next_filter_->SetURL(url); |
248 } | 302 } |
249 | 303 |
250 void Filter::SetMimeType(std::string& mime_type) { | 304 void Filter::SetMimeType(const std::string& mime_type) { |
251 mime_type_ = mime_type; | 305 mime_type_ = mime_type; |
252 if (next_filter_.get()) | 306 if (next_filter_.get()) |
253 next_filter_->SetMimeType(mime_type); | 307 next_filter_->SetMimeType(mime_type); |
254 } | 308 } |
255 | 309 |
OLD | NEW |