Skip to content

Handle MIME type parameter names case-insensitively - #37192

Open
lArtiquel wants to merge 1 commit into
spring-projects:mainfrom
lArtiquel:mimetype-parameter-name-case
Open

Handle MIME type parameter names case-insensitively#37192
lArtiquel wants to merge 1 commit into
spring-projects:mainfrom
lArtiquel:mimetype-parameter-name-case

Conversation

@lArtiquel

@lArtiquel lArtiquel commented Aug 22, 2026

Copy link
Copy Markdown

MimeType keeps parameters in a LinkedCaseInsensitiveMap, so equals() matches names case-insensitively. parametersHashCode() hashes the raw key, so it doesn't — which breaks the equals/hashCode contract:

MimeType a = MimeType.valueOf("text/plain;FOO=bar");
MimeType b = MimeType.valueOf("text/plain;foo=bar");

a.equals(b);                            // true
a.hashCode() == b.hashCode();           // false
new HashSet<>(List.of(a)).contains(b);  // false

The charset branch in equals, hashCode and compareTo keys off PARAM_CHARSET.equals(key), so a parameter spelled Charset skips it and is compared as an opaque string, even though the constructor already resolved it through the same case-insensitive map:

MimeType.valueOf("text/plain;charset=UTF-8").equals(MimeType.valueOf("text/plain;charset=utf-8")); // true
MimeType.valueOf("text/plain;Charset=UTF-8").equals(MimeType.valueOf("text/plain;Charset=utf-8")); // false

MediaType has the same assumption in two more places: an out-of-range quality value escapes validation when spelled Q=1.1, and removeQualityValue() leaves a Q= parameter in place.

Fix is six lines — equalsIgnoreCase in the four comparisons, and a normalized key before hashing.

Two behavior changes, both bringing the uppercase spelling in line with the lowercase one: Charset=UTF-8 now equals Charset=utf-8, and Q=1.1 is now rejected at parse time. Four tests added, each failing before the change; spring-core, -web, -webmvc, -webflux, -messaging and -test suites pass.

MIME type parameter names are case-insensitive, and MimeType already
stores them in a LinkedCaseInsensitiveMap. Several code paths, however,
still compared them with case-sensitive String.equals().

As a result, MimeType.hashCode() disagreed with MimeType.equals() for
parameter names that differ only in case, breaking the equals/hashCode
contract: text/plain;FOO=bar and text/plain;foo=bar are equal but hash
differently, so one is not found in a hash-based collection holding the
other. MimeType.compareTo() had the same blind spot for the charset
parameter.

MediaType was affected in two further ways: an out-of-range quality
value escaped validation when spelled Q=, and removeQualityValue() left
a Q= parameter in place.

Signed-off-by: Artyom Tsvirko <36863599+lArtiquel@users.noreply.github.com>
@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged or decided on label Aug 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

status: waiting-for-triage An issue we've not yet triaged or decided on

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants