Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(163)

Side by Side Diff: third_party/WebKit/Source/platform/network/ParsedContentType.cpp

Issue 2708523003: Make ParsedContentType more conformant to the spec (Closed)
Patch Set: fix Created 3 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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
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
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698