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

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"
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
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698