OLD | NEW |
1 /* | 1 /* |
2 * Copyright (C) 2011 Google Inc. All rights reserved. | 2 * Copyright (C) 2011 Google Inc. All rights reserved. |
3 * Copyright (C) 2012 Intel Corporation. All rights reserved. | 3 * Copyright (C) 2012 Intel Corporation. All rights reserved. |
4 * | 4 * |
5 * Redistribution and use in source and binary forms, with or without | 5 * Redistribution and use in source and binary forms, with or without |
6 * modification, are permitted provided that the following conditions are | 6 * modification, are permitted provided that the following conditions are |
7 * met: | 7 * met: |
8 * | 8 * |
9 * * Redistributions of source code must retain the above copyright | 9 * * Redistributions of source code must retain the above copyright |
10 * notice, this list of conditions and the following disclaimer. | 10 * notice, this list of conditions and the following disclaimer. |
(...skipping 13 matching lines...) Expand all Loading... |
24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | 24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | 25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | 26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
30 */ | 30 */ |
31 | 31 |
32 #include "platform/network/ParsedContentType.h" | 32 #include "platform/network/ParsedContentType.h" |
33 | 33 |
34 #include "wtf/text/CString.h" | |
35 #include "wtf/text/StringBuilder.h" | 34 #include "wtf/text/StringBuilder.h" |
| 35 #include "wtf/text/StringView.h" |
36 | 36 |
37 namespace blink { | 37 namespace blink { |
38 | 38 |
39 using SubstringRange = ParsedContentType::SubstringRange; | 39 using Mode = ParsedContentType::Mode; |
40 | 40 |
41 namespace { | 41 namespace { |
42 | 42 |
43 void skipSpaces(const String& input, unsigned& startIndex) { | 43 bool isTokenCharacter(Mode mode, UChar c) { |
44 while (startIndex < input.length() && input[startIndex] == ' ') | 44 if (c >= 128) |
45 ++startIndex; | 45 return false; |
| 46 if (c < 0x20) |
| 47 return false; |
| 48 |
| 49 switch (c) { |
| 50 case ' ': |
| 51 case ';': |
| 52 case '"': |
| 53 return false; |
| 54 case '(': |
| 55 case ')': |
| 56 case '<': |
| 57 case '>': |
| 58 case '@': |
| 59 case ',': |
| 60 case ':': |
| 61 case '\\': |
| 62 case '/': |
| 63 case '[': |
| 64 case ']': |
| 65 case '?': |
| 66 case '=': |
| 67 return mode == Mode::Relaxed; |
| 68 default: |
| 69 return true; |
| 70 } |
46 } | 71 } |
47 | 72 |
48 SubstringRange parseParameterPart(const String& input, unsigned& startIndex) { | 73 bool consume(char c, const String& input, unsigned& index) { |
49 unsigned inputLength = input.length(); | 74 DCHECK_NE(c, ' '); |
50 unsigned tokenStart = startIndex; | 75 while (index < input.length() && input[index] == ' ') |
51 unsigned& tokenEnd = startIndex; | 76 ++index; |
52 | 77 |
53 if (tokenEnd >= inputLength) | 78 if (index < input.length() && input[index] == c) { |
54 return SubstringRange(); | 79 ++index; |
55 | 80 return true; |
56 bool quoted = input[tokenStart] == '\"'; | |
57 bool escape = false; | |
58 | |
59 while (tokenEnd < inputLength) { | |
60 UChar c = input[tokenEnd]; | |
61 if (quoted && tokenStart != tokenEnd && c == '\"' && !escape) | |
62 return SubstringRange(tokenStart + 1, tokenEnd++ - tokenStart - 1); | |
63 if (!quoted && (c == ';' || c == '=')) | |
64 return SubstringRange(tokenStart, tokenEnd - tokenStart); | |
65 escape = !escape && c == '\\'; | |
66 ++tokenEnd; | |
67 } | 81 } |
68 | 82 return false; |
69 if (quoted) | |
70 return SubstringRange(); | |
71 return SubstringRange(tokenStart, tokenEnd - tokenStart); | |
72 } | 83 } |
73 | 84 |
74 String substringForRange(const String& string, const SubstringRange& range) { | 85 bool consumeToken(Mode mode, |
75 return string.substring(range.first, range.second); | 86 const String& input, |
| 87 unsigned& index, |
| 88 StringView& output) { |
| 89 DCHECK(output.isNull()); |
| 90 |
| 91 while (index < input.length() && input[index] == ' ') |
| 92 ++index; |
| 93 |
| 94 auto start = index; |
| 95 while (index < input.length() && isTokenCharacter(mode, input[index])) |
| 96 ++index; |
| 97 |
| 98 if (start == index) |
| 99 return false; |
| 100 |
| 101 output = StringView(input, start, index - start); |
| 102 return true; |
| 103 } |
| 104 |
| 105 bool consumeQuotedString(const String& input, unsigned& index, String& output) { |
| 106 StringBuilder builder; |
| 107 DCHECK_EQ('"', input[index]); |
| 108 ++index; |
| 109 while (index < input.length()) { |
| 110 if (input[index] == '\\') { |
| 111 ++index; |
| 112 if (index == input.length()) |
| 113 return false; |
| 114 builder.append(input[index]); |
| 115 ++index; |
| 116 continue; |
| 117 } |
| 118 if (input[index] == '"') { |
| 119 output = builder.toString(); |
| 120 ++index; |
| 121 return true; |
| 122 } |
| 123 builder.append(input[index]); |
| 124 ++index; |
| 125 } |
| 126 return false; |
| 127 } |
| 128 |
| 129 bool consumeTokenOrQuotedString(Mode mode, |
| 130 const String& input, |
| 131 unsigned& index, |
| 132 String& output) { |
| 133 while (index < input.length() && input[index] == ' ') |
| 134 ++index; |
| 135 if (input.length() == index) |
| 136 return false; |
| 137 if (input[index] == '"') { |
| 138 return consumeQuotedString(input, index, output); |
| 139 } |
| 140 StringView view; |
| 141 auto result = consumeToken(mode, input, index, view); |
| 142 output = view.toString(); |
| 143 return result; |
| 144 } |
| 145 |
| 146 bool isEnd(const String& input, unsigned index) { |
| 147 while (index < input.length()) { |
| 148 if (input[index] != ' ') |
| 149 return false; |
| 150 ++index; |
| 151 } |
| 152 return true; |
76 } | 153 } |
77 | 154 |
78 } // namespace | 155 } // namespace |
79 | 156 |
80 ParsedContentType::ParsedContentType(const String& contentType) { | 157 ParsedContentType::ParsedContentType(const String& contentType, Mode mode) |
81 if (contentType.contains('\r') || contentType.contains('\n')) | 158 : m_mode(mode) { |
82 m_isValid = false; | 159 m_isValid = parse(contentType); |
83 else | |
84 m_isValid = parse(contentType.stripWhiteSpace()); | |
85 } | 160 } |
86 | 161 |
87 String ParsedContentType::charset() const { | 162 String ParsedContentType::charset() const { |
88 return parameterValueForName("charset"); | 163 return parameterValueForName("charset"); |
89 } | 164 } |
90 | 165 |
91 String ParsedContentType::parameterValueForName(const String& name) const { | 166 String ParsedContentType::parameterValueForName(const String& name) const { |
92 return m_parameters.get(name); | 167 return m_parameters.get(name); |
93 } | 168 } |
94 | 169 |
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
137 // or tspecials> | 212 // or tspecials> |
138 // | 213 // |
139 // tspecials := "(" / ")" / "<" / ">" / "@" / | 214 // tspecials := "(" / ")" / "<" / ">" / "@" / |
140 // "," / ";" / ":" / "\" / <"> | 215 // "," / ";" / ":" / "\" / <"> |
141 // "/" / "[" / "]" / "?" / "=" | 216 // "/" / "[" / "]" / "?" / "=" |
142 // ; Must be in quoted-string, | 217 // ; Must be in quoted-string, |
143 // ; to use within parameter values | 218 // ; to use within parameter values |
144 | 219 |
145 bool ParsedContentType::parse(const String& contentType) { | 220 bool ParsedContentType::parse(const String& contentType) { |
146 unsigned index = 0; | 221 unsigned index = 0; |
147 unsigned contentTypeLength = contentType.length(); | 222 |
148 skipSpaces(contentType, index); | 223 StringView type, subtype; |
149 if (index >= contentTypeLength) { | 224 if (!consumeToken(Mode::Normal, contentType, index, type)) { |
150 DVLOG(1) << "Invalid Content-Type string '" << contentType << "'"; | 225 DVLOG(1) << "Failed to find `type' in '" << contentType << "'"; |
| 226 return false; |
| 227 } |
| 228 if (!consume('/', contentType, index)) { |
| 229 DVLOG(1) << "Failed to find '/' in '" << contentType << "'"; |
| 230 return false; |
| 231 } |
| 232 if (!consumeToken(Mode::Normal, contentType, index, subtype)) { |
| 233 DVLOG(1) << "Failed to find `type' in '" << contentType << "'"; |
151 return false; | 234 return false; |
152 } | 235 } |
153 | 236 |
154 // There should not be any quoted strings until we reach the parameters. | 237 StringBuilder builder; |
155 size_t semiColonIndex = contentType.find(';', index); | 238 builder.append(type); |
156 if (semiColonIndex == kNotFound) { | 239 builder.append('/'); |
157 m_mimeType = | 240 builder.append(subtype); |
158 substringForRange(contentType, | 241 m_mimeType = builder.toString(); |
159 SubstringRange(index, contentTypeLength - index)) | |
160 .stripWhiteSpace(); | |
161 return true; | |
162 } | |
163 | 242 |
164 m_mimeType = substringForRange(contentType, | 243 KeyValuePairs map; |
165 SubstringRange(index, semiColonIndex - index)) | 244 while (!isEnd(contentType, index)) { |
166 .stripWhiteSpace(); | 245 if (!consume(';', contentType, index)) { |
167 index = semiColonIndex + 1; | 246 DVLOG(1) << "Failed to find ';'"; |
168 do { | 247 return false; |
169 skipSpaces(contentType, index); | 248 } |
170 SubstringRange keyRange = parseParameterPart(contentType, index); | 249 |
171 if (!keyRange.second || index >= contentTypeLength) { | 250 StringView key; |
| 251 String value; |
| 252 if (!consumeToken(Mode::Normal, contentType, index, key)) { |
172 DVLOG(1) << "Invalid Content-Type parameter name. (at " << index << ")"; | 253 DVLOG(1) << "Invalid Content-Type parameter name. (at " << index << ")"; |
173 return false; | 254 return false; |
174 } | 255 } |
175 | 256 if (!consume('=', contentType, index)) { |
176 // Should we tolerate spaces here? | 257 DVLOG(1) << "Failed to find '='"; |
177 if (contentType[index++] != '=' || index >= contentTypeLength) { | |
178 DVLOG(1) << "Invalid Content-Type malformed parameter (at " << index | |
179 << ")."; | |
180 return false; | 258 return false; |
181 } | 259 } |
182 | 260 if (!consumeTokenOrQuotedString(m_mode, contentType, index, value)) { |
183 // Should we tolerate spaces here? | |
184 SubstringRange valueRange = parseParameterPart(contentType, index); | |
185 | |
186 if (!valueRange.second) { | |
187 DVLOG(1) << "Invalid Content-Type, invalid parameter value (at " << index | 261 DVLOG(1) << "Invalid Content-Type, invalid parameter value (at " << index |
188 << ", for '" | 262 << ", for '" << key.toString() << "')."; |
189 << substringForRange(contentType, keyRange).stripWhiteSpace() | |
190 << "')."; | |
191 return false; | 263 return false; |
192 } | 264 } |
193 | 265 map.set(key.toString(), value); |
194 // Should we tolerate spaces here? | 266 } |
195 if (index < contentTypeLength && contentType[index++] != ';') { | 267 m_parameters = std::move(map); |
196 DVLOG(1) << "Invalid Content-Type, invalid character at the end of " | |
197 "key/value parameter (at " | |
198 << index << ")."; | |
199 return false; | |
200 } | |
201 | |
202 m_parameters.set(substringForRange(contentType, keyRange), | |
203 substringForRange(contentType, valueRange)); | |
204 } while (index < contentTypeLength); | |
205 | |
206 return true; | 268 return true; |
207 } | 269 } |
208 | 270 |
209 } // namespace blink | 271 } // namespace blink |
OLD | NEW |