diff --git a/spring-core/src/main/java/org/springframework/util/MimeType.java b/spring-core/src/main/java/org/springframework/util/MimeType.java index 3c96f445e2ab..91373194ee8a 100644 --- a/spring-core/src/main/java/org/springframework/util/MimeType.java +++ b/spring-core/src/main/java/org/springframework/util/MimeType.java @@ -486,9 +486,9 @@ public boolean equals(@Nullable Object other) { /** * Determine if the parameters in this {@code MimeType} and the supplied * {@code MimeType} are equal, performing case-insensitive comparisons - * for {@link Charset Charsets} and disregarding quoting of parameter - * values, so that, for example, {@code spring="framework"} and - * {@code spring=framework} are considered equal. + * for parameter names and {@link Charset Charsets}, and disregarding + * quoting of parameter values, so that, for example, {@code spring="framework"} + * and {@code spring=framework} are considered equal. * @since 4.2 */ private boolean parametersAreEqual(MimeType other) { @@ -501,7 +501,7 @@ private boolean parametersAreEqual(MimeType other) { if (!other.parameters.containsKey(key)) { return false; } - if (PARAM_CHARSET.equals(key)) { + if (PARAM_CHARSET.equalsIgnoreCase(key)) { if (!ObjectUtils.nullSafeEquals(getCharset(), other.getCharset())) { return false; } @@ -528,15 +528,15 @@ public int hashCode() { /** * Compute a hash code for the parameters map, consistent with - * {@link #parametersAreEqual}: normalizing {@link Charset Charsets} and - * disregarding quoting of parameter values. + * {@link #parametersAreEqual}: normalizing parameter names and + * {@link Charset Charsets}, and disregarding quoting of parameter values. */ private int parametersHashCode() { int result = 0; for (Map.Entry entry : this.parameters.entrySet()) { String key = entry.getKey(); - Object value = (PARAM_CHARSET.equals(key) ? getCharset() : unquote(entry.getValue())); - result += key.hashCode() ^ ObjectUtils.nullSafeHashCode(value); + Object value = (PARAM_CHARSET.equalsIgnoreCase(key) ? getCharset() : unquote(entry.getValue())); + result += key.toLowerCase(Locale.ROOT).hashCode() ^ ObjectUtils.nullSafeHashCode(value); } return result; } @@ -602,7 +602,7 @@ public int compareTo(MimeType other) { if (comp != 0) { return comp; } - if (PARAM_CHARSET.equals(thisAttribute)) { + if (PARAM_CHARSET.equalsIgnoreCase(thisAttribute)) { Charset thisCharset = getCharset(); Charset otherCharset = other.getCharset(); if (thisCharset != otherCharset) { diff --git a/spring-core/src/test/java/org/springframework/util/MimeTypeTests.java b/spring-core/src/test/java/org/springframework/util/MimeTypeTests.java index bef9ebfde422..801794b1dfbd 100644 --- a/spring-core/src/test/java/org/springframework/util/MimeTypeTests.java +++ b/spring-core/src/test/java/org/springframework/util/MimeTypeTests.java @@ -539,6 +539,28 @@ void equalsIsCaseInsensitiveForCharsets() { assertThat(m2.compareTo(m1)).isEqualTo(0); } + @Test + void equalsIsCaseInsensitiveForParameterNames() { + MimeType m1 = new MimeType("text", "plain", singletonMap("Spring", "framework")); + MimeType m2 = new MimeType("text", "plain", singletonMap("spring", "framework")); + assertThat(m1).isEqualTo(m2); + assertThat(m2).isEqualTo(m1); + assertThat(m1).hasSameHashCodeAs(m2); + assertThat(m1.compareTo(m2)).isEqualTo(0); + assertThat(m2.compareTo(m1)).isEqualTo(0); + } + + @Test + void equalsIsCaseInsensitiveForCharsetParameterName() { + MimeType m1 = new MimeType("text", "plain", singletonMap("Charset", "UTF-8")); + MimeType m2 = new MimeType("text", "plain", singletonMap("charset", "utf-8")); + assertThat(m1).isEqualTo(m2); + assertThat(m2).isEqualTo(m1); + assertThat(m1).hasSameHashCodeAs(m2); + assertThat(m1.compareTo(m2)).isEqualTo(0); + assertThat(m2.compareTo(m1)).isEqualTo(0); + } + @Test // gh-36729 void equalsIgnoresParameterValueQuoting() { MimeType m1 = MimeTypeUtils.parseMimeType("text/plain; spring=\"framework\""); diff --git a/spring-web/src/main/java/org/springframework/http/MediaType.java b/spring-web/src/main/java/org/springframework/http/MediaType.java index b65e8053c944..72599932b634 100644 --- a/spring-web/src/main/java/org/springframework/http/MediaType.java +++ b/spring-web/src/main/java/org/springframework/http/MediaType.java @@ -503,7 +503,7 @@ public MediaType(MimeType mimeType) { @Override protected void checkParameters(String parameter, String value) { super.checkParameters(parameter, value); - if (PARAM_QUALITY_FACTOR.equals(parameter)) { + if (PARAM_QUALITY_FACTOR.equalsIgnoreCase(parameter)) { String unquotedValue = unquote(value); double d = Double.parseDouble(unquotedValue); Assert.isTrue(d >= 0D && d <= 1D, @@ -651,7 +651,7 @@ public MediaType removeQualityValue() { return this; } Map params = new LinkedHashMap<>(getParameters()); - params.remove(PARAM_QUALITY_FACTOR); + params.keySet().removeIf(PARAM_QUALITY_FACTOR::equalsIgnoreCase); return new MediaType(this, params); } diff --git a/spring-web/src/test/java/org/springframework/http/MediaTypeTests.java b/spring-web/src/test/java/org/springframework/http/MediaTypeTests.java index a7c9c090fb5a..6640b725a831 100644 --- a/spring-web/src/test/java/org/springframework/http/MediaTypeTests.java +++ b/spring-web/src/test/java/org/springframework/http/MediaTypeTests.java @@ -135,6 +135,12 @@ void parseMediaTypeIllegalQualityFactor() { MediaType.parseMediaType("audio/basic;q=1.1")); } + @Test + void parseMediaTypeIllegalQualityFactorWithUpperCaseParameterName() { + assertThatExceptionOfType(InvalidMediaTypeException.class).isThrownBy(() -> + MediaType.parseMediaType("audio/basic;Q=1.1")); + } + @Test void parseMediaTypeIllegalCharset() { assertThatExceptionOfType(InvalidMediaTypeException.class).isThrownBy(() -> @@ -293,6 +299,18 @@ void isConcrete() { assertThat(new MediaType("text", "*").isConcrete()).as("text/* concrete").isFalse(); } + @Test + void removeQualityValue() { + assertThat(MediaType.parseMediaType("audio/basic;q=0.8").removeQualityValue()) + .isEqualTo(MediaType.parseMediaType("audio/basic")); + } + + @Test + void removeQualityValueWithUpperCaseParameterName() { + assertThat(MediaType.parseMediaType("audio/basic;Q=0.8").removeQualityValue()) + .isEqualTo(MediaType.parseMediaType("audio/basic")); + } + @Test // gh-26127 void serialize() throws Exception { MediaType original = new MediaType("text", "plain", StandardCharsets.UTF_8);