Skip to content

Commit 84d8135

Browse files
committed
MimeType parsing properly handles quoted semicolons
Issue: SPR-14986 (cherry picked from commit 7714eec)
1 parent dd3c370 commit 84d8135

File tree

3 files changed

+47
-21
lines changed

3 files changed

+47
-21
lines changed

spring-core/src/main/java/org/springframework/util/MimeType.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -165,8 +165,8 @@ public MimeType(MimeType other, Map<String, String> parameters) {
165165
* @throws IllegalArgumentException if any of the parameters contains illegal characters
166166
*/
167167
public MimeType(String type, String subtype, Map<String, String> parameters) {
168-
Assert.hasLength(type, "type must not be empty");
169-
Assert.hasLength(subtype, "subtype must not be empty");
168+
Assert.hasLength(type, "'type' must not be empty");
169+
Assert.hasLength(subtype, "'subtype' must not be empty");
170170
checkToken(type);
171171
checkToken(subtype);
172172
this.type = type.toLowerCase(Locale.ENGLISH);
@@ -202,8 +202,8 @@ private void checkToken(String token) {
202202
}
203203

204204
protected void checkParameters(String attribute, String value) {
205-
Assert.hasLength(attribute, "parameter attribute must not be empty");
206-
Assert.hasLength(value, "parameter value must not be empty");
205+
Assert.hasLength(attribute, "'attribute' must not be empty");
206+
Assert.hasLength(value, "'value' must not be empty");
207207
checkToken(attribute);
208208
if (PARAM_CHARSET.equals(attribute)) {
209209
value = unquote(value);
@@ -277,8 +277,8 @@ public String getSubtype() {
277277
* @since 4.3
278278
*/
279279
public Charset getCharset() {
280-
String charSet = getParameter(PARAM_CHARSET);
281-
return (charSet != null ? Charset.forName(unquote(charSet)) : null);
280+
String charset = getParameter(PARAM_CHARSET);
281+
return (charset != null ? Charset.forName(unquote(charset)) : null);
282282
}
283283

284284
/**

spring-core/src/main/java/org/springframework/util/MimeTypeUtils.java

Lines changed: 31 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,10 @@ public abstract class MimeTypeUtils {
4949

5050
private static Charset US_ASCII = Charset.forName("US-ASCII");
5151

52+
/**
53+
* Comparator used by {@link #sortBySpecificity(List)}.
54+
*/
55+
public static final Comparator<MimeType> SPECIFICITY_COMPARATOR = new SpecificityComparator<MimeType>();
5256

5357
/**
5458
* Public constant mime type that includes all media ranges (i.e. "&#42;/&#42;").
@@ -219,12 +223,13 @@ public static MimeType parseMimeType(String mimeType) {
219223
if (!StringUtils.hasLength(mimeType)) {
220224
throw new InvalidMimeTypeException(mimeType, "'mimeType' must not be empty");
221225
}
222-
String[] parts = StringUtils.tokenizeToStringArray(mimeType, ";");
223-
if (parts.length == 0) {
226+
227+
int index = mimeType.indexOf(';');
228+
String fullType = (index >= 0 ? mimeType.substring(0, index) : mimeType).trim();
229+
if (fullType.length() == 0) {
224230
throw new InvalidMimeTypeException(mimeType, "'mimeType' must not be empty");
225231
}
226232

227-
String fullType = parts[0].trim();
228233
// java.net.HttpURLConnection returns a *; q=.2 Accept header
229234
if (MimeType.WILDCARD_TYPE.equals(fullType)) {
230235
fullType = "*/*";
@@ -243,18 +248,36 @@ public static MimeType parseMimeType(String mimeType) {
243248
}
244249

245250
Map<String, String> parameters = null;
246-
if (parts.length > 1) {
247-
parameters = new LinkedHashMap<String, String>(parts.length - 1);
248-
for (int i = 1; i < parts.length; i++) {
249-
String parameter = parts[i];
251+
do {
252+
int nextIndex = index + 1;
253+
boolean quoted = false;
254+
while (nextIndex < mimeType.length()) {
255+
char ch = mimeType.charAt(nextIndex);
256+
if (ch == ';') {
257+
if (!quoted) {
258+
break;
259+
}
260+
}
261+
else if (ch == '"') {
262+
quoted = !quoted;
263+
}
264+
nextIndex++;
265+
}
266+
String parameter = mimeType.substring(index + 1, nextIndex).trim();
267+
if (parameter.length() > 0) {
268+
if (parameters == null) {
269+
parameters = new LinkedHashMap<String, String>(4);
270+
}
250271
int eqIndex = parameter.indexOf('=');
251-
if (eqIndex != -1) {
272+
if (eqIndex >= 0) {
252273
String attribute = parameter.substring(0, eqIndex);
253274
String value = parameter.substring(eqIndex + 1, parameter.length());
254275
parameters.put(attribute, value);
255276
}
256277
}
278+
index = nextIndex;
257279
}
280+
while (index < mimeType.length());
258281

259282
try {
260283
return new MimeType(type, subtype, parameters);
@@ -353,11 +376,4 @@ public static String generateMultipartBoundaryString() {
353376
return new String(generateMultipartBoundary(), US_ASCII);
354377
}
355378

356-
357-
358-
/**
359-
* Comparator used by {@link #sortBySpecificity(List)}.
360-
*/
361-
public static final Comparator<MimeType> SPECIFICITY_COMPARATOR = new SpecificityComparator<MimeType>();
362-
363379
}

spring-core/src/test/java/org/springframework/util/MimeTypeTests.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,16 @@ public void parseQuotedCharset() {
8787
assertEquals("Invalid charset", Charset.forName("UTF-8"), mimeType.getCharset());
8888
}
8989

90+
@Test
91+
public void parseQuotedSeparator() {
92+
String s = "application/xop+xml;charset=utf-8;type=\"application/soap+xml;action=\\\"http://x.y.z\\\"\"";
93+
MimeType mimeType = MimeType.valueOf(s);
94+
assertEquals("Invalid type", "application", mimeType.getType());
95+
assertEquals("Invalid subtype", "xop+xml", mimeType.getSubtype());
96+
assertEquals("Invalid charset", Charset.forName("UTF-8"), mimeType.getCharset());
97+
assertEquals("\"application/soap+xml;action=\\\"http://x.y.z\\\"\"", mimeType.getParameter("type"));
98+
}
99+
90100
@Test
91101
public void withConversionService() {
92102
ConversionService conversionService = new DefaultConversionService();

0 commit comments

Comments
 (0)