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 18 matching lines...) Expand all Loading... | |
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" | 34 #include "wtf/text/CString.h" |
35 #include "wtf/text/StringBuilder.h" | 35 #include "wtf/text/StringBuilder.h" |
36 | 36 |
37 namespace blink { | 37 namespace blink { |
38 | 38 |
39 class DummyParsedContentType final { | 39 using SubstringRange = ParsedContentType::SubstringRange; |
40 STACK_ALLOCATED(); | |
41 | 40 |
42 public: | 41 namespace { |
43 void setContentType(const SubstringRange&) const {} | |
44 void setContentTypeParameter(const SubstringRange&, | |
45 const SubstringRange&) const {} | |
46 }; | |
47 | 42 |
48 static void skipSpaces(const String& input, unsigned& startIndex) { | 43 void skipSpaces(const String& input, unsigned& startIndex) { |
49 while (startIndex < input.length() && input[startIndex] == ' ') | 44 while (startIndex < input.length() && input[startIndex] == ' ') |
50 ++startIndex; | 45 ++startIndex; |
51 } | 46 } |
52 | 47 |
53 static SubstringRange parseParameterPart(const String& input, | 48 SubstringRange parseParameterPart(const String& input, unsigned& startIndex) { |
54 unsigned& startIndex) { | |
55 unsigned inputLength = input.length(); | 49 unsigned inputLength = input.length(); |
56 unsigned tokenStart = startIndex; | 50 unsigned tokenStart = startIndex; |
57 unsigned& tokenEnd = startIndex; | 51 unsigned& tokenEnd = startIndex; |
58 | 52 |
59 if (tokenEnd >= inputLength) | 53 if (tokenEnd >= inputLength) |
60 return SubstringRange(); | 54 return SubstringRange(); |
61 | 55 |
62 bool quoted = input[tokenStart] == '\"'; | 56 bool quoted = input[tokenStart] == '\"'; |
63 bool escape = false; | 57 bool escape = false; |
64 | 58 |
65 while (tokenEnd < inputLength) { | 59 while (tokenEnd < inputLength) { |
66 UChar c = input[tokenEnd]; | 60 UChar c = input[tokenEnd]; |
67 if (quoted && tokenStart != tokenEnd && c == '\"' && !escape) | 61 if (quoted && tokenStart != tokenEnd && c == '\"' && !escape) |
68 return SubstringRange(tokenStart + 1, tokenEnd++ - tokenStart - 1); | 62 return SubstringRange(tokenStart + 1, tokenEnd++ - tokenStart - 1); |
69 if (!quoted && (c == ';' || c == '=')) | 63 if (!quoted && (c == ';' || c == '=')) |
70 return SubstringRange(tokenStart, tokenEnd - tokenStart); | 64 return SubstringRange(tokenStart, tokenEnd - tokenStart); |
71 escape = !escape && c == '\\'; | 65 escape = !escape && c == '\\'; |
72 ++tokenEnd; | 66 ++tokenEnd; |
73 } | 67 } |
74 | 68 |
75 if (quoted) | 69 if (quoted) |
76 return SubstringRange(); | 70 return SubstringRange(); |
77 return SubstringRange(tokenStart, tokenEnd - tokenStart); | 71 return SubstringRange(tokenStart, tokenEnd - tokenStart); |
78 } | 72 } |
79 | 73 |
80 static String substringForRange(const String& string, | 74 String substringForRange(const String& string, const SubstringRange& range) { |
81 const SubstringRange& range) { | |
82 return string.substring(range.first, range.second); | 75 return string.substring(range.first, range.second); |
83 } | 76 } |
84 | 77 |
78 } // namespace | |
79 | |
80 ParsedContentType::ParsedContentType(const String& contentType) { | |
81 parse(contentType.stripWhiteSpace()); | |
tyoshino-do-not-use
2017/02/20 14:48:46
How about saving the result of parse() in a member
tyoshino-do-not-use
2017/02/20 14:50:31
Sorry. I forgot to insert break in the last paragr
yhirano
2017/02/21 04:40:30
Done.
| |
82 } | |
83 | |
84 String ParsedContentType::charset() const { | |
85 return parameterValueForName("charset"); | |
86 } | |
87 | |
88 String ParsedContentType::parameterValueForName(const String& name) const { | |
89 return m_parameters.get(name); | |
90 } | |
91 | |
92 size_t ParsedContentType::parameterCount() const { | |
93 return m_parameters.size(); | |
94 } | |
95 | |
96 bool ParsedContentType::isValid(const String& contentType) { | |
97 if (contentType.contains('\r') || contentType.contains('\n')) | |
98 return false; | |
99 | |
100 return ParsedContentType().parse(contentType.stripWhiteSpace()); | |
101 } | |
102 | |
85 // From http://tools.ietf.org/html/rfc2045#section-5.1: | 103 // From http://tools.ietf.org/html/rfc2045#section-5.1: |
86 // | 104 // |
87 // content := "Content-Type" ":" type "/" subtype | 105 // content := "Content-Type" ":" type "/" subtype |
88 // *(";" parameter) | 106 // *(";" parameter) |
89 // ; Matching of media type and subtype | 107 // ; Matching of media type and subtype |
90 // ; is ALWAYS case-insensitive. | 108 // ; is ALWAYS case-insensitive. |
91 // | 109 // |
92 // type := discrete-type / composite-type | 110 // type := discrete-type / composite-type |
93 // | 111 // |
94 // discrete-type := "text" / "image" / "audio" / "video" / | 112 // discrete-type := "text" / "image" / "audio" / "video" / |
(...skipping 26 matching lines...) Expand all Loading... | |
121 // | 139 // |
122 // token := 1*<any (US-ASCII) CHAR except SPACE, CTLs, | 140 // token := 1*<any (US-ASCII) CHAR except SPACE, CTLs, |
123 // or tspecials> | 141 // or tspecials> |
124 // | 142 // |
125 // tspecials := "(" / ")" / "<" / ">" / "@" / | 143 // tspecials := "(" / ")" / "<" / ">" / "@" / |
126 // "," / ";" / ":" / "\" / <"> | 144 // "," / ";" / ":" / "\" / <"> |
127 // "/" / "[" / "]" / "?" / "=" | 145 // "/" / "[" / "]" / "?" / "=" |
128 // ; Must be in quoted-string, | 146 // ; Must be in quoted-string, |
129 // ; to use within parameter values | 147 // ; to use within parameter values |
130 | 148 |
131 template <class ReceiverType> | 149 bool ParsedContentType::parse(const String& contentType) { |
132 bool parseContentType(const String& contentType, ReceiverType& receiver) { | |
133 unsigned index = 0; | 150 unsigned index = 0; |
134 unsigned contentTypeLength = contentType.length(); | 151 unsigned contentTypeLength = contentType.length(); |
135 skipSpaces(contentType, index); | 152 skipSpaces(contentType, index); |
136 if (index >= contentTypeLength) { | 153 if (index >= contentTypeLength) { |
137 DVLOG(1) << "Invalid Content-Type string '" << contentType << "'"; | 154 DVLOG(1) << "Invalid Content-Type string '" << contentType << "'"; |
138 return false; | 155 return false; |
139 } | 156 } |
140 | 157 |
141 // There should not be any quoted strings until we reach the parameters. | 158 // There should not be any quoted strings until we reach the parameters. |
142 size_t semiColonIndex = contentType.find(';', index); | 159 size_t semiColonIndex = contentType.find(';', index); |
143 if (semiColonIndex == kNotFound) { | 160 if (semiColonIndex == kNotFound) { |
144 receiver.setContentType(SubstringRange(index, contentTypeLength - index)); | 161 m_mimeType = |
162 substringForRange(contentType, | |
163 SubstringRange(index, contentTypeLength - index)) | |
164 .stripWhiteSpace(); | |
145 return true; | 165 return true; |
146 } | 166 } |
147 | 167 |
148 receiver.setContentType(SubstringRange(index, semiColonIndex - index)); | 168 m_mimeType = substringForRange(contentType, |
169 SubstringRange(index, semiColonIndex - index)) | |
170 .stripWhiteSpace(); | |
149 index = semiColonIndex + 1; | 171 index = semiColonIndex + 1; |
150 while (true) { | 172 do { |
151 skipSpaces(contentType, index); | 173 skipSpaces(contentType, index); |
152 SubstringRange keyRange = parseParameterPart(contentType, index); | 174 SubstringRange keyRange = parseParameterPart(contentType, index); |
153 if (!keyRange.second || index >= contentTypeLength) { | 175 if (!keyRange.second || index >= contentTypeLength) { |
154 DVLOG(1) << "Invalid Content-Type parameter name. (at " << index << ")"; | 176 DVLOG(1) << "Invalid Content-Type parameter name. (at " << index << ")"; |
155 return false; | 177 return false; |
156 } | 178 } |
157 | 179 |
158 // Should we tolerate spaces here? | 180 // Should we tolerate spaces here? |
159 if (contentType[index++] != '=' || index >= contentTypeLength) { | 181 if (contentType[index++] != '=' || index >= contentTypeLength) { |
160 DVLOG(1) << "Invalid Content-Type malformed parameter (at " << index | 182 DVLOG(1) << "Invalid Content-Type malformed parameter (at " << index |
(...skipping 13 matching lines...) Expand all Loading... | |
174 } | 196 } |
175 | 197 |
176 // Should we tolerate spaces here? | 198 // Should we tolerate spaces here? |
177 if (index < contentTypeLength && contentType[index++] != ';') { | 199 if (index < contentTypeLength && contentType[index++] != ';') { |
178 DVLOG(1) << "Invalid Content-Type, invalid character at the end of " | 200 DVLOG(1) << "Invalid Content-Type, invalid character at the end of " |
179 "key/value parameter (at " | 201 "key/value parameter (at " |
180 << index << ")."; | 202 << index << ")."; |
181 return false; | 203 return false; |
182 } | 204 } |
183 | 205 |
184 receiver.setContentTypeParameter(keyRange, valueRange); | 206 m_parameters.set(substringForRange(contentType, keyRange), |
185 | 207 substringForRange(contentType, valueRange)); |
186 if (index >= contentTypeLength) | 208 } while (index < contentTypeLength); |
187 return true; | |
188 } | |
189 | 209 |
190 return true; | 210 return true; |
191 } | 211 } |
192 | 212 |
193 bool isValidContentType(const String& contentType) { | |
194 if (contentType.contains('\r') || contentType.contains('\n')) | |
195 return false; | |
196 | |
197 DummyParsedContentType parsedContentType = DummyParsedContentType(); | |
198 return parseContentType<DummyParsedContentType>(contentType, | |
199 parsedContentType); | |
200 } | |
201 | |
202 ParsedContentType::ParsedContentType(const String& contentType) | |
203 : m_contentType(contentType.stripWhiteSpace()) { | |
204 parseContentType<ParsedContentType>(m_contentType, *this); | |
205 } | |
206 | |
207 String ParsedContentType::charset() const { | |
208 return parameterValueForName("charset"); | |
209 } | |
210 | |
211 String ParsedContentType::parameterValueForName(const String& name) const { | |
212 return m_parameters.get(name); | |
213 } | |
214 | |
215 size_t ParsedContentType::parameterCount() const { | |
216 return m_parameters.size(); | |
217 } | |
218 | |
219 void ParsedContentType::setContentType(const SubstringRange& contentRange) { | |
220 m_mimeType = substringForRange(m_contentType, contentRange).stripWhiteSpace(); | |
221 } | |
222 | |
223 void ParsedContentType::setContentTypeParameter(const SubstringRange& key, | |
224 const SubstringRange& value) { | |
225 m_parameters.set(substringForRange(m_contentType, key), | |
226 substringForRange(m_contentType, value)); | |
227 } | |
228 | |
229 } // namespace blink | 213 } // namespace blink |
OLD | NEW |