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" |
36 | 35 |
37 namespace blink { | 36 namespace blink { |
38 | 37 |
39 using SubstringRange = ParsedContentType::SubstringRange; | 38 using Mode = ParsedContentType::Mode; |
40 | 39 |
41 namespace { | 40 namespace { |
42 | 41 |
43 void skipSpaces(const String& input, unsigned& startIndex) { | 42 bool isTokenCharacter(Mode mode, UChar c) { |
44 while (startIndex < input.length() && input[startIndex] == ' ') | 43 if (c >= 128) |
45 ++startIndex; | 44 return false; |
45 if (c < 0x20) | |
46 return false; | |
47 | |
48 switch (c) { | |
49 case ' ': | |
50 case ';': | |
51 case '"': | |
52 return false; | |
53 case '(': | |
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 return mode == Mode::Relaxed; | |
67 default: | |
68 return true; | |
69 } | |
46 } | 70 } |
47 | 71 |
48 SubstringRange parseParameterPart(const String& input, unsigned& startIndex) { | 72 bool consume(char c, const String& input, unsigned& index) { |
49 unsigned inputLength = input.length(); | 73 DCHECK_NE(c, ' '); |
50 unsigned tokenStart = startIndex; | 74 while (index < input.length() && input[index] == ' ') |
51 unsigned& tokenEnd = startIndex; | 75 ++index; |
52 | 76 |
53 if (tokenEnd >= inputLength) | 77 if (index < input.length() && input[index] == c) { |
54 return SubstringRange(); | 78 ++index; |
55 | 79 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 } | 80 } |
68 | 81 return false; |
69 if (quoted) | |
70 return SubstringRange(); | |
71 return SubstringRange(tokenStart, tokenEnd - tokenStart); | |
72 } | 82 } |
73 | 83 |
74 String substringForRange(const String& string, const SubstringRange& range) { | 84 bool consumeToken(Mode mode, |
75 return string.substring(range.first, range.second); | 85 const String& input, |
86 unsigned& index, | |
87 String& output) { | |
88 DCHECK(output.isNull()); | |
89 | |
90 while (index < input.length() && input[index] == ' ') | |
91 ++index; | |
92 | |
93 auto start = index; | |
94 while (index < input.length() && isTokenCharacter(mode, input[index])) | |
95 ++index; | |
96 | |
97 if (start == index) | |
98 return false; | |
99 | |
100 output = input.substring(start, index - start); | |
kinuko
2017/02/21 11:43:02
nit: output could be StringView if we want to save
yhirano
2017/02/22 05:17:03
Done.
| |
101 return true; | |
102 } | |
103 | |
104 bool consumeQuotedString(const String& input, unsigned& index, String& output) { | |
105 StringBuilder builder; | |
106 DCHECK_EQ('"', input[index]); | |
107 ++index; | |
108 while (index < input.length()) { | |
109 if (input[index] == '\\') { | |
110 ++index; | |
111 if (index == input.length()) | |
112 return false; | |
113 builder.append(input[index]); | |
114 ++index; | |
115 continue; | |
116 } | |
117 if (input[index] == '"') { | |
118 output = builder.toString(); | |
119 ++index; | |
120 return true; | |
121 } | |
122 builder.append(input[index]); | |
123 ++index; | |
124 } | |
125 return false; | |
126 } | |
127 | |
128 bool consumeTokenOrQuotedString(Mode mode, | |
129 const String& input, | |
130 unsigned& index, | |
131 String& output) { | |
132 while (index < input.length() && input[index] == ' ') | |
133 ++index; | |
134 if (input.length() == index) | |
135 return false; | |
136 if (input[index] == '"') | |
137 return consumeQuotedString(input, index, output); | |
138 return consumeToken(mode, input, index, output); | |
139 } | |
140 | |
141 bool isEnd(const String& input, unsigned index) { | |
kinuko
2017/02/21 11:43:02
Any reason this doesn't take unsigned& index?
yhirano
2017/02/22 05:17:03
consume* eat the input and hence update the index.
| |
142 while (index < input.length()) { | |
143 if (input[index] != ' ') | |
144 return false; | |
145 ++index; | |
146 } | |
147 return true; | |
76 } | 148 } |
77 | 149 |
78 } // namespace | 150 } // namespace |
79 | 151 |
80 ParsedContentType::ParsedContentType(const String& contentType) { | 152 ParsedContentType::ParsedContentType(const String& contentType, Mode mode) |
81 if (contentType.contains('\r') || contentType.contains('\n')) | 153 : m_mode(mode) { |
82 m_isValid = false; | 154 m_isValid = parse(contentType); |
83 else | |
84 m_isValid = parse(contentType.stripWhiteSpace()); | |
85 } | 155 } |
86 | 156 |
87 String ParsedContentType::charset() const { | 157 String ParsedContentType::charset() const { |
88 return parameterValueForName("charset"); | 158 return parameterValueForName("charset"); |
89 } | 159 } |
90 | 160 |
91 String ParsedContentType::parameterValueForName(const String& name) const { | 161 String ParsedContentType::parameterValueForName(const String& name) const { |
92 return m_parameters.get(name); | 162 return m_parameters.get(name); |
93 } | 163 } |
94 | 164 |
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
137 // or tspecials> | 207 // or tspecials> |
138 // | 208 // |
139 // tspecials := "(" / ")" / "<" / ">" / "@" / | 209 // tspecials := "(" / ")" / "<" / ">" / "@" / |
140 // "," / ";" / ":" / "\" / <"> | 210 // "," / ";" / ":" / "\" / <"> |
141 // "/" / "[" / "]" / "?" / "=" | 211 // "/" / "[" / "]" / "?" / "=" |
142 // ; Must be in quoted-string, | 212 // ; Must be in quoted-string, |
143 // ; to use within parameter values | 213 // ; to use within parameter values |
144 | 214 |
145 bool ParsedContentType::parse(const String& contentType) { | 215 bool ParsedContentType::parse(const String& contentType) { |
146 unsigned index = 0; | 216 unsigned index = 0; |
147 unsigned contentTypeLength = contentType.length(); | 217 |
148 skipSpaces(contentType, index); | 218 String type, subtype; |
149 if (index >= contentTypeLength) { | 219 if (!consumeToken(Mode::Normal, contentType, index, type)) { |
150 DVLOG(1) << "Invalid Content-Type string '" << contentType << "'"; | 220 DVLOG(1) << "Failed to find `type' in '" << contentType << "'"; |
221 return false; | |
222 } | |
223 if (!consume('/', contentType, index)) { | |
224 DVLOG(1) << "Failed to find '/' in '" << contentType << "'"; | |
225 return false; | |
226 } | |
227 if (!consumeToken(Mode::Normal, contentType, index, subtype)) { | |
228 DVLOG(1) << "Failed to find `type' in '" << contentType << "'"; | |
151 return false; | 229 return false; |
152 } | 230 } |
153 | 231 |
154 // There should not be any quoted strings until we reach the parameters. | 232 m_mimeType = type + '/' + subtype; |
155 size_t semiColonIndex = contentType.find(';', index); | |
156 if (semiColonIndex == kNotFound) { | |
157 m_mimeType = | |
158 substringForRange(contentType, | |
159 SubstringRange(index, contentTypeLength - index)) | |
160 .stripWhiteSpace(); | |
161 return true; | |
162 } | |
163 | 233 |
164 m_mimeType = substringForRange(contentType, | 234 KeyValuePairs map; |
165 SubstringRange(index, semiColonIndex - index)) | 235 while (!isEnd(contentType, index)) { |
166 .stripWhiteSpace(); | 236 if (!consume(';', contentType, index)) { |
167 index = semiColonIndex + 1; | 237 DVLOG(1) << "Failed to find ';'"; |
168 do { | 238 return false; |
169 skipSpaces(contentType, index); | 239 } |
170 SubstringRange keyRange = parseParameterPart(contentType, index); | 240 |
171 if (!keyRange.second || index >= contentTypeLength) { | 241 String key, value; |
242 if (!consumeToken(Mode::Normal, contentType, index, key)) { | |
172 DVLOG(1) << "Invalid Content-Type parameter name. (at " << index << ")"; | 243 DVLOG(1) << "Invalid Content-Type parameter name. (at " << index << ")"; |
173 return false; | 244 return false; |
174 } | 245 } |
175 | 246 if (!consume('=', contentType, index)) { |
176 // Should we tolerate spaces here? | 247 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; | 248 return false; |
181 } | 249 } |
182 | 250 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 | 251 DVLOG(1) << "Invalid Content-Type, invalid parameter value (at " << index |
188 << ", for '" | 252 << ", for '" << key << "')."; |
189 << substringForRange(contentType, keyRange).stripWhiteSpace() | |
190 << "')."; | |
191 return false; | 253 return false; |
192 } | 254 } |
193 | 255 map.set(key, value); |
194 // Should we tolerate spaces here? | 256 } |
195 if (index < contentTypeLength && contentType[index++] != ';') { | 257 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; | 258 return true; |
207 } | 259 } |
208 | 260 |
209 } // namespace blink | 261 } // namespace blink |
OLD | NEW |