diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/codec/CodecProperties.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/codec/CodecProperties.java new file mode 100644 index 0000000000..dcec543919 --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/codec/CodecProperties.java @@ -0,0 +1,46 @@ +/* + * Copyright 2012-2019 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. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.autoconfigure.codec; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.util.unit.DataSize; + +/** + * {@link ConfigurationProperties properties} for reactive codecs. + * + * @author Brian Clozel + * @since 2.2.1 + */ +@ConfigurationProperties(prefix = "spring.codec") +public class CodecProperties { + + /** + * Limit on the number of bytes that can be buffered whenever the input stream needs + * to be aggregated. By default this is not set, in which case individual codec + * defaults apply. Most codecs are limited to 256K by default. + */ + private DataSize maxInMemorySize; + + public DataSize getMaxInMemorySize() { + return this.maxInMemorySize; + } + + public void setMaxInMemorySize(DataSize maxInMemorySize) { + this.maxInMemorySize = maxInMemorySize; + } + +} diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/codec/package-info.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/codec/package-info.java new file mode 100644 index 0000000000..5acd780bbc --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/codec/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright 2012-2019 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. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Auto-configuration for reactive codecs. + */ +package org.springframework.boot.autoconfigure.codec; diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/http/codec/CodecsAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/http/codec/CodecsAutoConfiguration.java index 2d10e79ba3..9fbc6be3b8 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/http/codec/CodecsAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/http/codec/CodecsAutoConfiguration.java @@ -20,11 +20,13 @@ import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.codec.CodecProperties; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.http.HttpProperties; import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration; import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.boot.context.properties.PropertyMapper; import org.springframework.boot.web.codec.CodecCustomizer; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -33,6 +35,7 @@ import org.springframework.http.codec.CodecConfigurer; import org.springframework.http.codec.json.Jackson2JsonDecoder; import org.springframework.http.codec.json.Jackson2JsonEncoder; import org.springframework.util.MimeType; +import org.springframework.util.unit.DataSize; import org.springframework.web.reactive.function.client.WebClient; /** @@ -46,6 +49,7 @@ import org.springframework.web.reactive.function.client.WebClient; @Configuration(proxyBeanMethods = false) @ConditionalOnClass({ CodecConfigurer.class, WebClient.class }) @AutoConfigureAfter(JacksonAutoConfiguration.class) +@EnableConfigurationProperties({ HttpProperties.class, CodecProperties.class }) public class CodecsAutoConfiguration { private static final MimeType[] EMPTY_MIME_TYPES = {}; @@ -68,14 +72,18 @@ public class CodecsAutoConfiguration { } @Configuration(proxyBeanMethods = false) - @EnableConfigurationProperties(HttpProperties.class) - static class LoggingCodecConfiguration { + static class DefaultCodecsConfiguration { @Bean @Order(0) - CodecCustomizer loggingCodecCustomizer(HttpProperties properties) { - return (configurer) -> configurer.defaultCodecs() - .enableLoggingRequestDetails(properties.isLogRequestDetails()); + CodecCustomizer defaultCodecCustomizer(HttpProperties httpProperties, CodecProperties codecProperties) { + return (configurer) -> { + PropertyMapper map = PropertyMapper.get(); + CodecConfigurer.DefaultCodecs defaultCodecs = configurer.defaultCodecs(); + defaultCodecs.enableLoggingRequestDetails(httpProperties.isLogRequestDetails()); + map.from(codecProperties.getMaxInMemorySize()).whenNonNull().asInt(DataSize::toBytes) + .to(defaultCodecs::maxInMemorySize); + }; } } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/http/codec/CodecsAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/http/codec/CodecsAutoConfigurationTests.java index 500f47f192..fcd5b78359 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/http/codec/CodecsAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/http/codec/CodecsAutoConfigurationTests.java @@ -22,6 +22,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.Test; import org.springframework.boot.autoconfigure.AutoConfigurations; +import org.springframework.boot.autoconfigure.codec.CodecProperties; import org.springframework.boot.autoconfigure.http.HttpProperties; import org.springframework.boot.test.context.runner.WebApplicationContextRunner; import org.springframework.boot.web.codec.CodecCustomizer; @@ -67,11 +68,11 @@ class CodecsAutoConfigurationTests { } @Test - void loggingRequestDetailsBeanShouldHaveOrderZero() { + void defaultCodecCustomizerBeanShouldHaveOrderZero() { this.contextRunner.run((context) -> { Method customizerMethod = ReflectionUtils.findMethod( - CodecsAutoConfiguration.LoggingCodecConfiguration.class, "loggingCodecCustomizer", - HttpProperties.class); + CodecsAutoConfiguration.DefaultCodecsConfiguration.class, "defaultCodecCustomizer", + HttpProperties.class, CodecProperties.class); Integer order = new TestAnnotationAwareOrderComparator().findOrder(customizerMethod); assertThat(order).isEqualTo(0); }); @@ -98,6 +99,16 @@ class CodecsAutoConfigurationTests { }); } + @Test + void maxInMemorySizeEnforcedInDefaultCodecs() { + this.contextRunner.withPropertyValues("spring.codec.max-in-memory-size=1MB").run((context) -> { + CodecCustomizer customizer = context.getBean(CodecCustomizer.class); + CodecConfigurer configurer = new DefaultClientCodecConfigurer(); + customizer.customize(configurer); + assertThat(configurer.defaultCodecs()).hasFieldOrPropertyWithValue("maxInMemorySize", 1048576); + }); + } + static class TestAnnotationAwareOrderComparator extends AnnotationAwareOrderComparator { @Override diff --git a/spring-boot-project/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc b/spring-boot-project/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc index 0b3520ec53..d4ccec5768 100644 --- a/spring-boot-project/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc +++ b/spring-boot-project/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc @@ -2538,8 +2538,9 @@ If you want to take complete control of Spring WebFlux, you can add your own `@C Spring WebFlux uses the `HttpMessageReader` and `HttpMessageWriter` interfaces to convert HTTP requests and responses. They are configured with `CodecConfigurer` to have sensible defaults by looking at the libraries available in your classpath. -Spring Boot applies further customization by using `CodecCustomizer` instances. -For example, `spring.jackson.*` configuration keys are applied to the Jackson codec. +Spring Boot provides dedicated configuration properties for codecs, `+spring.codec.*+`. +It also applies further customization by using `CodecCustomizer` instances. +For example, `+spring.jackson.*+` configuration keys are applied to the Jackson codec. If you need to add or customize codecs, you can create a custom `CodecCustomizer` component, as shown in the following example: @@ -2554,7 +2555,7 @@ If you need to add or customize codecs, you can create a custom `CodecCustomizer public CodecCustomizer myCodecCustomizer() { return codecConfigurer -> { // ... - } + }; } } diff --git a/spring-boot-project/spring-boot-docs/src/main/groovy/generateConfigurationPropertyTables.groovy b/spring-boot-project/spring-boot-docs/src/main/groovy/generateConfigurationPropertyTables.groovy index 56313059b3..1b5baa2fdc 100644 --- a/spring-boot-project/spring-boot-docs/src/main/groovy/generateConfigurationPropertyTables.groovy +++ b/spring-boot-project/spring-boot-docs/src/main/groovy/generateConfigurationPropertyTables.groovy @@ -24,7 +24,7 @@ def generateConfigMetadataDocumentation() { builder .addSection("core") .withKeyPrefixes("debug", "trace", "logging", "spring.aop", "spring.application", - "spring.autoconfigure", "spring.banner", "spring.beaninfo", "spring.config", + "spring.autoconfigure", "spring.banner", "spring.beaninfo", "spring.codec", "spring.config", "spring.info", "spring.jmx", "spring.main", "spring.messages", "spring.pid", "spring.profiles", "spring.quartz", "spring.reactor", "spring.task", "spring.mandatory-file-encoding", "info", "spring.output.ansi.enabled")