Skip to content

Commit a21df0c

Browse files
committed
Provide default codecs config callback to custom codecs
As a follow-up of gh-23961, this change provides a way for custom codecs to align with the default codecs' behavior on common features like buffer size limits and logging request details. Closes gh-24119 Co-authored-by: Rossen Stoyanchev <[email protected]>
1 parent 83683a1 commit a21df0c

File tree

5 files changed

+100
-6
lines changed

5 files changed

+100
-6
lines changed

spring-web/src/main/java/org/springframework/http/codec/CodecConfigurer.java

+34
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,11 @@
1717
package org.springframework.http.codec;
1818

1919
import java.util.List;
20+
import java.util.function.Consumer;
2021

2122
import org.springframework.core.codec.Decoder;
2223
import org.springframework.core.codec.Encoder;
24+
import org.springframework.lang.Nullable;
2325

2426
/**
2527
* Defines a common interface for configuring either client or server HTTP
@@ -213,6 +215,38 @@ interface CustomCodecs {
213215
* @param writer the writer to add
214216
*/
215217
void writer(HttpMessageWriter<?> writer);
218+
219+
/**
220+
* Register a callback for the {@link DefaultCodecConfig configuration}
221+
* applied to default codecs. This allows custom codecs to follow general
222+
* guidelines applied to default ones, such as logging details and limiting
223+
* the amount of buffered data.
224+
* @param codecsConfigConsumer the default codecs configuration callback
225+
* @since 5.1.12
226+
*/
227+
void withDefaultCodecConfig(Consumer<DefaultCodecConfig> codecsConfigConsumer);
228+
}
229+
230+
231+
/**
232+
* Common options applied to default codecs and passed in a callback to custom codecs
233+
* so they get a chance to align their behavior on the default ones.
234+
* @since 5.1.12
235+
*/
236+
interface DefaultCodecConfig {
237+
238+
/**
239+
* Get the configured limit on the number of bytes that can be buffered whenever
240+
* the input stream needs to be aggregated.
241+
*/
242+
@Nullable
243+
Integer maxInMemorySize();
244+
245+
/**
246+
* Whether to log form data at DEBUG level, and headers at TRACE level.
247+
* Both may contain sensitive information.
248+
*/
249+
boolean isEnableLoggingRequestDetails();
216250
}
217251

218252
}

spring-web/src/main/java/org/springframework/http/codec/support/BaseCodecConfigurer.java

+17
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
import java.util.ArrayList;
2020
import java.util.List;
21+
import java.util.function.Consumer;
2122

2223
import org.springframework.core.ResolvableType;
2324
import org.springframework.core.codec.Decoder;
@@ -39,6 +40,8 @@
3940
*/
4041
abstract class BaseCodecConfigurer implements CodecConfigurer {
4142

43+
protected boolean customCodecsInitialized;
44+
4245
protected final BaseDefaultCodecs defaultCodecs;
4346

4447
protected final DefaultCustomCodecs customCodecs;
@@ -88,6 +91,7 @@ public CustomCodecs customCodecs() {
8891

8992
@Override
9093
public List<HttpMessageReader<?>> getReaders() {
94+
initializeCustomCodecs();
9195
List<HttpMessageReader<?>> result = new ArrayList<>();
9296

9397
result.addAll(this.defaultCodecs.getTypedReaders());
@@ -113,6 +117,7 @@ public List<HttpMessageWriter<?>> getWriters() {
113117
* same except for the multipart writer itself.
114118
*/
115119
protected List<HttpMessageWriter<?>> getWritersInternal(boolean forMultipart) {
120+
initializeCustomCodecs();
116121
List<HttpMessageWriter<?>> result = new ArrayList<>();
117122

118123
result.addAll(this.defaultCodecs.getTypedWriters(forMultipart));
@@ -128,6 +133,13 @@ protected List<HttpMessageWriter<?>> getWritersInternal(boolean forMultipart) {
128133
@Override
129134
public abstract CodecConfigurer clone();
130135

136+
private void initializeCustomCodecs() {
137+
if(!this.customCodecsInitialized) {
138+
this.customCodecs.configConsumers.forEach(consumer -> consumer.accept(this.defaultCodecs));
139+
this.customCodecsInitialized = true;
140+
}
141+
}
142+
131143

132144
/**
133145
* Default implementation of {@code CustomCodecs}.
@@ -142,6 +154,7 @@ protected static final class DefaultCustomCodecs implements CustomCodecs {
142154

143155
private final List<HttpMessageWriter<?>> objectWriters = new ArrayList<>();
144156

157+
private final List<Consumer<DefaultCodecConfig>> configConsumers = new ArrayList<>();
145158

146159
DefaultCustomCodecs() {
147160
}
@@ -179,6 +192,10 @@ public void writer(HttpMessageWriter<?> writer) {
179192
(canWriteObject ? this.objectWriters : this.typedWriters).add(writer);
180193
}
181194

195+
@Override
196+
public void withDefaultCodecConfig(Consumer<DefaultCodecConfig> codecsConfigConsumer) {
197+
this.configConsumers.add(codecsConfigConsumer);
198+
}
182199

183200
// Package private accessors...
184201

spring-web/src/main/java/org/springframework/http/codec/support/BaseDefaultCodecs.java

+5-3
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@
5959
* @author Rossen Stoyanchev
6060
* @author Sebastien Deleuze
6161
*/
62-
class BaseDefaultCodecs implements CodecConfigurer.DefaultCodecs {
62+
class BaseDefaultCodecs implements CodecConfigurer.DefaultCodecs, CodecConfigurer.DefaultCodecConfig {
6363

6464
static final boolean jackson2Present;
6565

@@ -158,8 +158,9 @@ public void maxInMemorySize(int byteCount) {
158158
this.maxInMemorySize = byteCount;
159159
}
160160

161+
@Override
161162
@Nullable
162-
protected Integer maxInMemorySize() {
163+
public Integer maxInMemorySize() {
163164
return this.maxInMemorySize;
164165
}
165166

@@ -168,7 +169,8 @@ public void enableLoggingRequestDetails(boolean enable) {
168169
this.enableLoggingRequestDetails = enable;
169170
}
170171

171-
protected boolean isEnableLoggingRequestDetails() {
172+
@Override
173+
public boolean isEnableLoggingRequestDetails() {
172174
return this.enableLoggingRequestDetails;
173175
}
174176

spring-web/src/test/java/org/springframework/http/codec/support/CodecConfigurerTests.java

+13
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
import java.util.Arrays;
2020
import java.util.List;
21+
import java.util.concurrent.atomic.AtomicBoolean;
2122
import java.util.concurrent.atomic.AtomicInteger;
2223
import java.util.stream.Collectors;
2324

@@ -336,6 +337,18 @@ public void cloneDefaultCodecs() {
336337
assertFalse(encoders.containsAll(Arrays.asList(jacksonEncoder, jaxb2Encoder, protoEncoder)));
337338
}
338339

340+
@Test
341+
public void withDefaultCodecConfig() {
342+
AtomicBoolean callbackCalled = new AtomicBoolean(false);
343+
this.configurer.defaultCodecs().enableLoggingRequestDetails(true);
344+
this.configurer.customCodecs().withDefaultCodecConfig(config -> {
345+
assertTrue(config.isEnableLoggingRequestDetails());
346+
callbackCalled.compareAndSet(false, true);
347+
});
348+
this.configurer.getReaders();
349+
assertTrue(callbackCalled.get());
350+
}
351+
339352
private Decoder<?> getNextDecoder(List<HttpMessageReader<?>> readers) {
340353
HttpMessageReader<?> reader = readers.get(this.index.getAndIncrement());
341354
assertEquals(DecoderHttpMessageReader.class, reader.getClass());

src/docs/asciidoc/web/webflux.adoc

+31-3
Original file line numberDiff line numberDiff line change
@@ -883,16 +883,44 @@ The following example shows how to do so for client-side requests:
883883
[subs="verbatim,quotes"]
884884
----
885885
Consumer<ClientCodecConfigurer> consumer = configurer ->
886-
configurer.defaultCodecs().enableLoggingRequestDetails(true);
886+
configurer.defaultCodecs().enableLoggingRequestDetails(true);
887887
888888
WebClient webClient = WebClient.builder()
889-
.exchangeStrategies(ExchangeStrategies.builder().codecs(consumer).build())
890-
.build();
889+
.exchangeStrategies(strategies -> strategies.codecs(consumer))
890+
.build();
891891
----
892892
====
893893

894+
[[webflux-codecs-custom]]
895+
==== Custom codecs
896+
897+
Applications can register custom codecs for supporting additional media types,
898+
or specific behaviors that are not supported by the default codecs.
899+
900+
Some configuration options expressed by developers are enforced on default codecs.
901+
Custom codecs might want to get a chance to align with those preferences,
902+
like <<webflux-codecs-limits, enforcing buffering limits>>
903+
or <<webflux-logging-sensitive-data, logging sensitive data>>.
894904

905+
The following example shows how to do so for client-side requests:
906+
907+
====
908+
[source,java,indent=0]
909+
[subs="verbatim,quotes"]
910+
----
911+
Consumer<ClientCodecConfigurer> consumer = configurer -> {
912+
CustomDecoder customDecoder = new CustomDecoder();
913+
configurer.customCodecs().decoder(customDecoder);
914+
configurer.customCodecs().withDefaultCodecConfig(config ->
915+
customDecoder.maxInMemorySize(config.maxInMemorySize())
916+
);
917+
}
895918
919+
WebClient webClient = WebClient.builder()
920+
.exchangeStrategies(strategies -> strategies.codecs(consumer))
921+
.build();
922+
----
923+
====
896924

897925
[[webflux-dispatcher-handler]]
898926
== `DispatcherHandler`

0 commit comments

Comments
 (0)