diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/ServerProperties.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/ServerProperties.java index 5a90266e6d3..0c2e2b04d40 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/ServerProperties.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/ServerProperties.java @@ -120,7 +120,7 @@ public class ServerProperties { /** * Custom MIME mappings in addition to the default MIME mappings. */ - private final MimeMappings mimeMappings = MimeMappings.lazyCopy(MimeMappings.DEFAULT); + private final MimeMappings mimeMappings = new MimeMappings(); @NestedConfigurationProperty private final Http2 http2 = new Http2(); diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/ServletWebServerFactoryCustomizer.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/ServletWebServerFactoryCustomizer.java index dafeecb4860..bc1dbd4e248 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/ServletWebServerFactoryCustomizer.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/ServletWebServerFactoryCustomizer.java @@ -95,7 +95,7 @@ public class ServletWebServerFactoryCustomizer map.from(() -> this.cookieSameSiteSuppliers) .whenNot(CollectionUtils::isEmpty) .to(factory::setCookieSameSiteSuppliers); - map.from(this.serverProperties::getMimeMappings).to(factory::setMimeMappings); + map.from(this.serverProperties::getMimeMappings).to(factory::addMimeMappings); this.webListenerRegistrars.forEach((registrar) -> registrar.register(factory)); } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/ServerPropertiesTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/ServerPropertiesTests.java index 18841f1c1e8..0bfeacf8ec6 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/ServerPropertiesTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/ServerPropertiesTests.java @@ -187,13 +187,12 @@ class ServerPropertiesTests { @Test void testDefaultMimeMapping() { - assertThat(this.properties.getMimeMappings()) - .containsExactly(MimeMappings.DEFAULT.getAll().toArray(new Mapping[0])); + assertThat(this.properties.getMimeMappings()).isEmpty(); } @Test void testCustomizedMimeMapping() { - MimeMappings expectedMappings = MimeMappings.lazyCopy(MimeMappings.DEFAULT); + MimeMappings expectedMappings = new MimeMappings(); expectedMappings.add("mjs", "text/javascript"); bind("server.mime-mappings.mjs", "text/javascript"); assertThat(this.properties.getMimeMappings()) diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/servlet/ServletWebServerFactoryCustomizerTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/servlet/ServletWebServerFactoryCustomizerTests.java index b29a52b2962..66e0ae47710 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/servlet/ServletWebServerFactoryCustomizerTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/servlet/ServletWebServerFactoryCustomizerTests.java @@ -22,6 +22,7 @@ import java.util.Map; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; import org.springframework.boot.autoconfigure.web.ServerProperties; import org.springframework.boot.context.properties.bind.Bindable; @@ -29,6 +30,7 @@ import org.springframework.boot.context.properties.bind.Binder; import org.springframework.boot.context.properties.source.ConfigurationPropertySource; import org.springframework.boot.context.properties.source.MapConfigurationPropertySource; import org.springframework.boot.web.server.Cookie; +import org.springframework.boot.web.server.MimeMappings; import org.springframework.boot.web.server.Shutdown; import org.springframework.boot.web.server.Ssl; import org.springframework.boot.web.servlet.server.ConfigurableServletWebServerFactory; @@ -74,10 +76,25 @@ class ServletWebServerFactoryCustomizerTests { } @Test - void testCustomMimeMappings() { + void withNoCustomMimeMappingsThenEmptyMimeMappingsIsAdded() { ConfigurableServletWebServerFactory factory = mock(ConfigurableServletWebServerFactory.class); this.customizer.customize(factory); - then(factory).should().setMimeMappings(this.properties.getMimeMappings()); + ArgumentCaptor mimeMappingsCaptor = ArgumentCaptor.forClass(MimeMappings.class); + then(factory).should().addMimeMappings(mimeMappingsCaptor.capture()); + MimeMappings mimeMappings = mimeMappingsCaptor.getValue(); + assertThat(mimeMappings.getAll()).isEmpty(); + } + + @Test + void withCustomMimeMappingsThenPopulatedMimeMappingsIsAdded() { + this.properties.getMimeMappings().add("a", "alpha"); + this.properties.getMimeMappings().add("b", "bravo"); + ConfigurableServletWebServerFactory factory = mock(ConfigurableServletWebServerFactory.class); + this.customizer.customize(factory); + ArgumentCaptor mimeMappingsCaptor = ArgumentCaptor.forClass(MimeMappings.class); + then(factory).should().addMimeMappings(mimeMappingsCaptor.capture()); + MimeMappings mimeMappings = mimeMappingsCaptor.getValue(); + assertThat(mimeMappings.getAll()).hasSize(2); } @Test diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/servlet/server/AbstractServletWebServerFactory.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/servlet/server/AbstractServletWebServerFactory.java index d34dabce332..4f3ef502c9d 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/servlet/server/AbstractServletWebServerFactory.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/servlet/server/AbstractServletWebServerFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -178,6 +178,11 @@ public abstract class AbstractServletWebServerFactory extends AbstractConfigurab this.mimeMappings = new MimeMappings(mimeMappings); } + @Override + public void addMimeMappings(MimeMappings mimeMappings) { + mimeMappings.forEach((mapping) -> this.mimeMappings.add(mapping.getExtension(), mapping.getMimeType())); + } + /** * Returns the document root which will be used by the web context to serve static * files. diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/servlet/server/ConfigurableServletWebServerFactory.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/servlet/server/ConfigurableServletWebServerFactory.java index 0aa2783446c..c4b442a45f2 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/servlet/server/ConfigurableServletWebServerFactory.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/servlet/server/ConfigurableServletWebServerFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2021 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -82,6 +82,13 @@ public interface ConfigurableServletWebServerFactory */ void setMimeMappings(MimeMappings mimeMappings); + /** + * Adds mime-type mappings. + * @param mimeMappings the mime type mappings to add + * @since 3.3.0 + */ + void addMimeMappings(MimeMappings mimeMappings); + /** * Sets the document root directory which will be used by the web context to serve * static files. diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/servlet/server/AbstractServletWebServerFactoryTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/servlet/server/AbstractServletWebServerFactoryTests.java index 0981a9d224a..37b9f6ba91b 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/servlet/server/AbstractServletWebServerFactoryTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/servlet/server/AbstractServletWebServerFactoryTests.java @@ -34,6 +34,7 @@ import java.security.NoSuchAlgorithmException; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.time.Duration; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Date; @@ -1010,6 +1011,23 @@ public abstract class AbstractServletWebServerFactoryTests { assertThat(configuredMimeMappings).containsExactlyInAnyOrderElementsOf(expectedMimeMappings); } + @Test + void additionalMimeMappingsCanBeConfigured() { + AbstractServletWebServerFactory factory = getFactory(); + MimeMappings additionalMimeMappings = new MimeMappings(); + additionalMimeMappings.add("a", "alpha"); + additionalMimeMappings.add("b", "bravo"); + factory.addMimeMappings(additionalMimeMappings); + this.webServer = factory.getWebServer(); + Collection configuredMimeMappings = getActualMimeMappings().entrySet() + .stream() + .map((entry) -> new MimeMappings.Mapping(entry.getKey(), entry.getValue())) + .toList(); + List expectedMimeMappings = new ArrayList<>(MimeMappings.DEFAULT.getAll()); + expectedMimeMappings.addAll(additionalMimeMappings.getAll()); + assertThat(configuredMimeMappings).containsExactlyInAnyOrderElementsOf(expectedMimeMappings); + } + @Test void rootServletContextResource() { AbstractServletWebServerFactory factory = getFactory();