From 0633bcc29359b8f5d94f998fa1cde4ae3ef625e6 Mon Sep 17 00:00:00 2001 From: Arjen Poutsma Date: Wed, 7 Dec 2022 13:06:39 +0100 Subject: [PATCH] Align multipart codecs on client and server This commit ensures that the same multipart codecs are registered on both client and server. Previously, only the client enabled only sending multipart, and the server only receiving. Closes gh-29630 --- .../http/codec/ClientCodecConfigurer.java | 32 +----- .../http/codec/CodecConfigurer.java | 41 +++++++ .../http/codec/ServerCodecConfigurer.java | 12 +-- .../multipart/MultipartHttpMessageWriter.java | 25 ++++- .../codec/support/BaseCodecConfigurer.java | 2 + .../http/codec/support/BaseDefaultCodecs.java | 102 ++++++++++++++++++ .../support/ClientDefaultCodecsImpl.java | 94 +--------------- .../support/DefaultClientCodecConfigurer.java | 17 +-- .../support/ServerDefaultCodecsImpl.java | 36 +------ .../support/ClientCodecConfigurerTests.java | 24 +++-- .../codec/support/CodecConfigurerTests.java | 26 ++++- .../support/ServerCodecConfigurerTests.java | 6 +- .../WebFluxConfigurationSupportTests.java | 4 +- 13 files changed, 216 insertions(+), 205 deletions(-) diff --git a/spring-web/src/main/java/org/springframework/http/codec/ClientCodecConfigurer.java b/spring-web/src/main/java/org/springframework/http/codec/ClientCodecConfigurer.java index 070b0610ee1f..cfba0520ea88 100644 --- a/spring-web/src/main/java/org/springframework/http/codec/ClientCodecConfigurer.java +++ b/spring-web/src/main/java/org/springframework/http/codec/ClientCodecConfigurer.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2022 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. @@ -17,7 +17,6 @@ package org.springframework.http.codec; import org.springframework.core.codec.Decoder; -import org.springframework.core.codec.Encoder; /** * Extension of {@link CodecConfigurer} for HTTP message reader and writer @@ -83,13 +82,6 @@ static ClientCodecConfigurer create() { */ interface ClientDefaultCodecs extends DefaultCodecs { - /** - * Configure encoders or writers for use with - * {@link org.springframework.http.codec.multipart.MultipartHttpMessageWriter - * MultipartHttpMessageWriter}. - */ - MultipartCodecs multipartCodecs(); - /** * Configure the {@code Decoder} to use for Server-Sent Events. *

By default if this is not set, and Jackson is available, the @@ -102,26 +94,4 @@ interface ClientDefaultCodecs extends DefaultCodecs { void serverSentEventDecoder(Decoder decoder); } - - /** - * Registry and container for multipart HTTP message writers. - */ - interface MultipartCodecs { - - /** - * Add a Part {@code Encoder}, internally wrapped with - * {@link EncoderHttpMessageWriter}. - * @param encoder the encoder to add - */ - MultipartCodecs encoder(Encoder encoder); - - /** - * Add a Part {@link HttpMessageWriter}. For writers of type - * {@link EncoderHttpMessageWriter} consider using the shortcut - * {@link #encoder(Encoder)} instead. - * @param writer the writer to add - */ - MultipartCodecs writer(HttpMessageWriter writer); - } - } diff --git a/spring-web/src/main/java/org/springframework/http/codec/CodecConfigurer.java b/spring-web/src/main/java/org/springframework/http/codec/CodecConfigurer.java index a27b0709a3aa..a5906a49b697 100644 --- a/spring-web/src/main/java/org/springframework/http/codec/CodecConfigurer.java +++ b/spring-web/src/main/java/org/springframework/http/codec/CodecConfigurer.java @@ -258,6 +258,24 @@ interface DefaultCodecs { * @since 5.1 */ void enableLoggingRequestDetails(boolean enable); + + /** + * Configure encoders or writers for use with + * {@link org.springframework.http.codec.multipart.MultipartHttpMessageWriter + * MultipartHttpMessageWriter}. + * @since 6.0.3 + */ + MultipartCodecs multipartCodecs(); + + /** + * Configure the {@code HttpMessageReader} to use for multipart requests. + *

Note that {@link #maxInMemorySize(int)} and/or + * {@link #enableLoggingRequestDetails(boolean)}, if configured, will be + * applied to the given reader, if applicable. + * @param reader the message reader to use for multipart requests. + * @since 6.0.3 + */ + void multipartReader(HttpMessageReader reader); } @@ -389,4 +407,27 @@ interface DefaultCodecConfig { Boolean isEnableLoggingRequestDetails(); } + + /** + * Registry and container for multipart HTTP message writers. + * @since 6.0.3 + */ + interface MultipartCodecs { + + /** + * Add a Part {@code Encoder}, internally wrapped with + * {@link EncoderHttpMessageWriter}. + * @param encoder the encoder to add + */ + MultipartCodecs encoder(Encoder encoder); + + /** + * Add a Part {@link HttpMessageWriter}. For writers of type + * {@link EncoderHttpMessageWriter} consider using the shortcut + * {@link #encoder(Encoder)} instead. + * @param writer the writer to add + */ + MultipartCodecs writer(HttpMessageWriter writer); + } + } diff --git a/spring-web/src/main/java/org/springframework/http/codec/ServerCodecConfigurer.java b/spring-web/src/main/java/org/springframework/http/codec/ServerCodecConfigurer.java index c6f9222a19c0..767ab738e0d2 100644 --- a/spring-web/src/main/java/org/springframework/http/codec/ServerCodecConfigurer.java +++ b/spring-web/src/main/java/org/springframework/http/codec/ServerCodecConfigurer.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2021 the original author or authors. + * Copyright 2002-2022 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,16 +82,6 @@ static ServerCodecConfigurer create() { */ interface ServerDefaultCodecs extends DefaultCodecs { - /** - * Configure the {@code HttpMessageReader} to use for multipart requests. - *

Note that {@link #maxInMemorySize(int)} and/or - * {@link #enableLoggingRequestDetails(boolean)}, if configured, will be - * applied to the given reader, if applicable. - * @param reader the message reader to use for multipart requests. - * @since 5.1.11 - */ - void multipartReader(HttpMessageReader reader); - /** * Configure the {@code Encoder} to use for Server-Sent Events. *

By default if this is not set, and Jackson is available, the diff --git a/spring-web/src/main/java/org/springframework/http/codec/multipart/MultipartHttpMessageWriter.java b/spring-web/src/main/java/org/springframework/http/codec/multipart/MultipartHttpMessageWriter.java index 777b5991ab86..b4fca60dd935 100644 --- a/spring-web/src/main/java/org/springframework/http/codec/multipart/MultipartHttpMessageWriter.java +++ b/spring-web/src/main/java/org/springframework/http/codec/multipart/MultipartHttpMessageWriter.java @@ -79,7 +79,7 @@ public class MultipartHttpMessageWriter extends MultipartWriterSupport private static final Map DEFAULT_HINTS = Hints.from(Hints.SUPPRESS_LOGGING_HINT, true); - private final List> partWriters; + private final Supplier>> partWritersSupplier; @Nullable private final HttpMessageWriter> formWriter; @@ -112,8 +112,23 @@ public MultipartHttpMessageWriter(List> partWriters) { public MultipartHttpMessageWriter(List> partWriters, @Nullable HttpMessageWriter> formWriter) { + this(() -> partWriters, formWriter); + } + + /** + * Constructor with a supplier for an explicit list of writers for + * serializing parts and a writer for plain form data to fall back when + * no media type is specified and the actual map consists of String + * values only. + * @param partWritersSupplier the supplier for writers for serializing parts + * @param formWriter the fallback writer for form data, {@code null} by default + * @since 6.0.3 + */ + public MultipartHttpMessageWriter(Supplier>> partWritersSupplier, + @Nullable HttpMessageWriter> formWriter) { + super(initMediaTypes(formWriter)); - this.partWriters = partWriters; + this.partWritersSupplier = partWritersSupplier; this.formWriter = formWriter; } @@ -131,7 +146,7 @@ private static List initMediaTypes(@Nullable HttpMessageWriter for * @since 5.0.7 */ public List> getPartWriters() { - return Collections.unmodifiableList(this.partWriters); + return Collections.unmodifiableList(this.partWritersSupplier.get()); } @@ -264,8 +279,8 @@ else if (resolvableType.resolve() == Resource.class) { MediaType contentType = headers.getContentType(); - final ResolvableType finalBodyType = resolvableType; - Optional> writer = this.partWriters.stream() + ResolvableType finalBodyType = resolvableType; + Optional> writer = this.partWritersSupplier.get().stream() .filter(partWriter -> partWriter.canWrite(finalBodyType, contentType)) .findFirst(); diff --git a/spring-web/src/main/java/org/springframework/http/codec/support/BaseCodecConfigurer.java b/spring-web/src/main/java/org/springframework/http/codec/support/BaseCodecConfigurer.java index 4eed384a6413..3b7b4ddb2e23 100644 --- a/spring-web/src/main/java/org/springframework/http/codec/support/BaseCodecConfigurer.java +++ b/spring-web/src/main/java/org/springframework/http/codec/support/BaseCodecConfigurer.java @@ -55,6 +55,7 @@ abstract class BaseCodecConfigurer implements CodecConfigurer { Assert.notNull(defaultCodecs, "'defaultCodecs' is required"); this.defaultCodecs = defaultCodecs; this.customCodecs = new DefaultCustomCodecs(); + this.defaultCodecs.setPartWritersSupplier(this::getWriters); } /** @@ -64,6 +65,7 @@ abstract class BaseCodecConfigurer implements CodecConfigurer { protected BaseCodecConfigurer(BaseCodecConfigurer other) { this.defaultCodecs = other.cloneDefaultCodecs(); this.customCodecs = new DefaultCustomCodecs(other.customCodecs); + this.defaultCodecs.setPartWritersSupplier(this::getWriters); } /** diff --git a/spring-web/src/main/java/org/springframework/http/codec/support/BaseDefaultCodecs.java b/spring-web/src/main/java/org/springframework/http/codec/support/BaseDefaultCodecs.java index 9cfe0f3d7fb6..05c4c0f4a4d1 100644 --- a/spring-web/src/main/java/org/springframework/http/codec/support/BaseDefaultCodecs.java +++ b/spring-web/src/main/java/org/springframework/http/codec/support/BaseDefaultCodecs.java @@ -21,6 +21,7 @@ import java.util.List; import java.util.Map; import java.util.function.Consumer; +import java.util.function.Supplier; import org.springframework.core.codec.AbstractDataBufferDecoder; import org.springframework.core.codec.ByteArrayDecoder; @@ -62,6 +63,8 @@ import org.springframework.http.codec.multipart.MultipartHttpMessageReader; import org.springframework.http.codec.multipart.MultipartHttpMessageWriter; import org.springframework.http.codec.multipart.PartEventHttpMessageReader; +import org.springframework.http.codec.multipart.PartEventHttpMessageWriter; +import org.springframework.http.codec.multipart.PartHttpMessageWriter; import org.springframework.http.codec.protobuf.KotlinSerializationProtobufDecoder; import org.springframework.http.codec.protobuf.KotlinSerializationProtobufEncoder; import org.springframework.http.codec.protobuf.ProtobufDecoder; @@ -160,6 +163,15 @@ class BaseDefaultCodecs implements CodecConfigurer.DefaultCodecs, CodecConfigure @Nullable private Encoder kotlinSerializationProtobufEncoder; + @Nullable + private DefaultMultipartCodecs multipartCodecs; + + @Nullable + private Supplier>> partWritersSupplier; + + @Nullable + private HttpMessageReader multipartReader; + @Nullable private Consumer codecConsumer; @@ -224,6 +236,9 @@ protected BaseDefaultCodecs(BaseDefaultCodecs other) { this.kotlinSerializationJsonEncoder = other.kotlinSerializationJsonEncoder; this.kotlinSerializationProtobufDecoder = other.kotlinSerializationProtobufDecoder; this.kotlinSerializationProtobufEncoder = other.kotlinSerializationProtobufEncoder; + this.multipartCodecs = other.multipartCodecs != null ? + new DefaultMultipartCodecs(other.multipartCodecs) : null; + this.multipartReader = other.multipartReader; this.codecConsumer = other.codecConsumer; this.maxInMemorySize = other.maxInMemorySize; this.enableLoggingRequestDetails = other.enableLoggingRequestDetails; @@ -351,6 +366,31 @@ public void enableLoggingRequestDetails(boolean enable) { } } + @Override + public CodecConfigurer.MultipartCodecs multipartCodecs() { + if (this.multipartCodecs == null) { + this.multipartCodecs = new DefaultMultipartCodecs(); + } + return this.multipartCodecs; + } + + @Override + public void multipartReader(HttpMessageReader multipartReader) { + this.multipartReader = multipartReader; + initTypedReaders(); + } + + /** + * Set a supplier for part writers to use when + * {@link #multipartCodecs()} are not explicitly configured. + * That's the same set of writers as for general except for the multipart + * writer itself. + */ + void setPartWritersSupplier(Supplier>> supplier) { + this.partWritersSupplier = supplier; + initTypedWriters(); + } + @Override @Nullable public Boolean isEnableLoggingRequestDetails() { @@ -405,6 +445,15 @@ else if (kotlinSerializationProtobufPresent) { (KotlinSerializationProtobufDecoder) this.kotlinSerializationProtobufDecoder : new KotlinSerializationProtobufDecoder())); } addCodec(this.typedReaders, new FormHttpMessageReader()); + if (this.multipartReader != null) { + addCodec(this.typedReaders, this.multipartReader); + } + else { + DefaultPartHttpMessageReader partReader = new DefaultPartHttpMessageReader(); + addCodec(this.typedReaders, partReader); + addCodec(this.typedReaders, new MultipartHttpMessageReader(partReader)); + } + addCodec(this.typedReaders, new PartEventHttpMessageReader()); // client vs server.. extendTypedReaders(this.typedReaders); @@ -641,9 +690,25 @@ final List> getBaseTypedWriters() { addCodec(writers, new ProtobufHttpMessageWriter(this.protobufEncoder != null ? (ProtobufEncoder) this.protobufEncoder : new ProtobufEncoder())); } + addCodec(writers, new MultipartHttpMessageWriter(this::getPartWriters, new FormHttpMessageWriter())); + addCodec(writers, new PartEventHttpMessageWriter()); + addCodec(writers, new PartHttpMessageWriter()); return writers; } + private List> getPartWriters() { + if (this.multipartCodecs != null) { + return this.multipartCodecs.getWriters(); + } + else if (this.partWritersSupplier != null) { + return this.partWritersSupplier.get(); + } + else { + return Collections.emptyList(); + } + } + + /** * Hook for client or server specific typed writers. */ @@ -766,4 +831,41 @@ protected Encoder getKotlinSerializationJsonEncoder() { return this.kotlinSerializationJsonEncoder; } + + /** + * Default implementation of {@link CodecConfigurer.MultipartCodecs}. + */ + protected class DefaultMultipartCodecs implements CodecConfigurer.MultipartCodecs { + + private final List> writers = new ArrayList<>(); + + + DefaultMultipartCodecs() { + } + + DefaultMultipartCodecs(DefaultMultipartCodecs other) { + this.writers.addAll(other.writers); + } + + + @Override + public CodecConfigurer.MultipartCodecs encoder(Encoder encoder) { + writer(new EncoderHttpMessageWriter<>(encoder)); + initTypedWriters(); + return this; + } + + @Override + public CodecConfigurer.MultipartCodecs writer(HttpMessageWriter writer) { + this.writers.add(writer); + initTypedWriters(); + return this; + } + + List> getWriters() { + return this.writers; + } + } + + } diff --git a/spring-web/src/main/java/org/springframework/http/codec/support/ClientDefaultCodecsImpl.java b/spring-web/src/main/java/org/springframework/http/codec/support/ClientDefaultCodecsImpl.java index c321682174b1..a494a74ccefe 100644 --- a/spring-web/src/main/java/org/springframework/http/codec/support/ClientDefaultCodecsImpl.java +++ b/spring-web/src/main/java/org/springframework/http/codec/support/ClientDefaultCodecsImpl.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2021 the original author or authors. + * Copyright 2002-2022 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. @@ -16,21 +16,12 @@ package org.springframework.http.codec.support; -import java.util.ArrayList; -import java.util.Collections; import java.util.List; -import java.util.function.Supplier; import org.springframework.core.codec.Decoder; -import org.springframework.core.codec.Encoder; import org.springframework.http.codec.ClientCodecConfigurer; -import org.springframework.http.codec.EncoderHttpMessageWriter; -import org.springframework.http.codec.FormHttpMessageWriter; import org.springframework.http.codec.HttpMessageReader; -import org.springframework.http.codec.HttpMessageWriter; import org.springframework.http.codec.ServerSentEventHttpMessageReader; -import org.springframework.http.codec.multipart.MultipartHttpMessageWriter; -import org.springframework.http.codec.multipart.PartEventHttpMessageWriter; import org.springframework.lang.Nullable; /** @@ -40,47 +31,18 @@ */ class ClientDefaultCodecsImpl extends BaseDefaultCodecs implements ClientCodecConfigurer.ClientDefaultCodecs { - @Nullable - private DefaultMultipartCodecs multipartCodecs; - @Nullable private Decoder sseDecoder; - @Nullable - private Supplier>> partWritersSupplier; - ClientDefaultCodecsImpl() { } ClientDefaultCodecsImpl(ClientDefaultCodecsImpl other) { super(other); - this.multipartCodecs = (other.multipartCodecs != null ? - new DefaultMultipartCodecs(other.multipartCodecs) : null); this.sseDecoder = other.sseDecoder; } - - /** - * Set a supplier for part writers to use when - * {@link #multipartCodecs()} are not explicitly configured. - * That's the same set of writers as for general except for the multipart - * writer itself. - */ - void setPartWritersSupplier(Supplier>> supplier) { - this.partWritersSupplier = supplier; - initTypedWriters(); - } - - - @Override - public ClientCodecConfigurer.MultipartCodecs multipartCodecs() { - if (this.multipartCodecs == null) { - this.multipartCodecs = new DefaultMultipartCodecs(); - } - return this.multipartCodecs; - } - @Override public void serverSentEventDecoder(Decoder decoder) { this.sseDecoder = decoder; @@ -98,58 +60,4 @@ protected void extendObjectReaders(List> objectReaders) { addCodec(objectReaders, new ServerSentEventHttpMessageReader(decoder)); } - @Override - protected void extendTypedWriters(List> typedWriters) { - addCodec(typedWriters, new MultipartHttpMessageWriter(getPartWriters(), new FormHttpMessageWriter())); - addCodec(typedWriters, new PartEventHttpMessageWriter()); - } - - private List> getPartWriters() { - if (this.multipartCodecs != null) { - return this.multipartCodecs.getWriters(); - } - else if (this.partWritersSupplier != null) { - return this.partWritersSupplier.get(); - } - else { - return Collections.emptyList(); - } - } - - - /** - * Default implementation of {@link ClientCodecConfigurer.MultipartCodecs}. - */ - private class DefaultMultipartCodecs implements ClientCodecConfigurer.MultipartCodecs { - - private final List> writers = new ArrayList<>(); - - - DefaultMultipartCodecs() { - } - - DefaultMultipartCodecs(DefaultMultipartCodecs other) { - this.writers.addAll(other.writers); - } - - - @Override - public ClientCodecConfigurer.MultipartCodecs encoder(Encoder encoder) { - writer(new EncoderHttpMessageWriter<>(encoder)); - initTypedWriters(); - return this; - } - - @Override - public ClientCodecConfigurer.MultipartCodecs writer(HttpMessageWriter writer) { - this.writers.add(writer); - initTypedWriters(); - return this; - } - - List> getWriters() { - return this.writers; - } - } - } diff --git a/spring-web/src/main/java/org/springframework/http/codec/support/DefaultClientCodecConfigurer.java b/spring-web/src/main/java/org/springframework/http/codec/support/DefaultClientCodecConfigurer.java index 382d11bec8c4..fc03ca5de384 100644 --- a/spring-web/src/main/java/org/springframework/http/codec/support/DefaultClientCodecConfigurer.java +++ b/spring-web/src/main/java/org/springframework/http/codec/support/DefaultClientCodecConfigurer.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2022 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. @@ -16,11 +16,7 @@ package org.springframework.http.codec.support; -import java.util.ArrayList; -import java.util.List; - import org.springframework.http.codec.ClientCodecConfigurer; -import org.springframework.http.codec.HttpMessageWriter; /** * Default implementation of {@link ClientCodecConfigurer}. @@ -33,12 +29,10 @@ public class DefaultClientCodecConfigurer extends BaseCodecConfigurer implements public DefaultClientCodecConfigurer() { super(new ClientDefaultCodecsImpl()); - ((ClientDefaultCodecsImpl) defaultCodecs()).setPartWritersSupplier(this::getPartWriters); } private DefaultClientCodecConfigurer(DefaultClientCodecConfigurer other) { super(other); - ((ClientDefaultCodecsImpl) defaultCodecs()).setPartWritersSupplier(this::getPartWriters); } @@ -57,14 +51,5 @@ protected BaseDefaultCodecs cloneDefaultCodecs() { return new ClientDefaultCodecsImpl((ClientDefaultCodecsImpl) defaultCodecs()); } - private List> getPartWriters() { - List> result = new ArrayList<>(); - result.addAll(this.customCodecs.getTypedWriters().keySet()); - result.addAll(this.defaultCodecs.getBaseTypedWriters()); - result.addAll(this.customCodecs.getObjectWriters().keySet()); - result.addAll(this.defaultCodecs.getBaseObjectWriters()); - result.addAll(this.defaultCodecs.getCatchAllWriters()); - return result; - } } diff --git a/spring-web/src/main/java/org/springframework/http/codec/support/ServerDefaultCodecsImpl.java b/spring-web/src/main/java/org/springframework/http/codec/support/ServerDefaultCodecsImpl.java index 186949655a4f..52c7bf407982 100644 --- a/spring-web/src/main/java/org/springframework/http/codec/support/ServerDefaultCodecsImpl.java +++ b/spring-web/src/main/java/org/springframework/http/codec/support/ServerDefaultCodecsImpl.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2021 the original author or authors. + * Copyright 2002-2022 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. @@ -19,14 +19,9 @@ import java.util.List; import org.springframework.core.codec.Encoder; -import org.springframework.http.codec.HttpMessageReader; import org.springframework.http.codec.HttpMessageWriter; import org.springframework.http.codec.ServerCodecConfigurer; import org.springframework.http.codec.ServerSentEventHttpMessageWriter; -import org.springframework.http.codec.multipart.DefaultPartHttpMessageReader; -import org.springframework.http.codec.multipart.MultipartHttpMessageReader; -import org.springframework.http.codec.multipart.PartEventHttpMessageReader; -import org.springframework.http.codec.multipart.PartHttpMessageWriter; import org.springframework.lang.Nullable; /** @@ -36,9 +31,6 @@ */ class ServerDefaultCodecsImpl extends BaseDefaultCodecs implements ServerCodecConfigurer.ServerDefaultCodecs { - @Nullable - private HttpMessageReader multipartReader; - @Nullable private Encoder sseEncoder; @@ -48,42 +40,16 @@ class ServerDefaultCodecsImpl extends BaseDefaultCodecs implements ServerCodecCo ServerDefaultCodecsImpl(ServerDefaultCodecsImpl other) { super(other); - this.multipartReader = other.multipartReader; this.sseEncoder = other.sseEncoder; } - @Override - public void multipartReader(HttpMessageReader reader) { - this.multipartReader = reader; - initTypedReaders(); - } - @Override public void serverSentEventEncoder(Encoder encoder) { this.sseEncoder = encoder; initObjectWriters(); } - - @Override - protected void extendTypedReaders(List> typedReaders) { - if (this.multipartReader != null) { - addCodec(typedReaders, this.multipartReader); - } - else { - DefaultPartHttpMessageReader partReader = new DefaultPartHttpMessageReader(); - addCodec(typedReaders, partReader); - addCodec(typedReaders, new MultipartHttpMessageReader(partReader)); - } - addCodec(typedReaders, new PartEventHttpMessageReader()); - } - - @Override - protected void extendTypedWriters(List> typedWriters) { - addCodec(typedWriters, new PartHttpMessageWriter()); - } - @Override protected void extendObjectWriters(List> objectWriters) { objectWriters.add(new ServerSentEventHttpMessageWriter(getSseEncoder())); diff --git a/spring-web/src/test/java/org/springframework/http/codec/support/ClientCodecConfigurerTests.java b/spring-web/src/test/java/org/springframework/http/codec/support/ClientCodecConfigurerTests.java index e061ba55f219..aadb220fd0f2 100644 --- a/spring-web/src/test/java/org/springframework/http/codec/support/ClientCodecConfigurerTests.java +++ b/spring-web/src/test/java/org/springframework/http/codec/support/ClientCodecConfigurerTests.java @@ -64,8 +64,12 @@ import org.springframework.http.codec.json.Jackson2SmileEncoder; import org.springframework.http.codec.json.KotlinSerializationJsonDecoder; import org.springframework.http.codec.json.KotlinSerializationJsonEncoder; +import org.springframework.http.codec.multipart.DefaultPartHttpMessageReader; +import org.springframework.http.codec.multipart.MultipartHttpMessageReader; import org.springframework.http.codec.multipart.MultipartHttpMessageWriter; +import org.springframework.http.codec.multipart.PartEventHttpMessageReader; import org.springframework.http.codec.multipart.PartEventHttpMessageWriter; +import org.springframework.http.codec.multipart.PartHttpMessageWriter; import org.springframework.http.codec.protobuf.KotlinSerializationProtobufDecoder; import org.springframework.http.codec.protobuf.KotlinSerializationProtobufEncoder; import org.springframework.http.codec.protobuf.ProtobufDecoder; @@ -92,7 +96,7 @@ public class ClientCodecConfigurerTests { @Test public void defaultReaders() { List> readers = this.configurer.getReaders(); - assertThat(readers).hasSize(17); + assertThat(readers).hasSize(20); assertThat(getNextDecoder(readers).getClass()).isEqualTo(ByteArrayDecoder.class); assertThat(getNextDecoder(readers).getClass()).isEqualTo(ByteBufferDecoder.class); assertThat(getNextDecoder(readers).getClass()).isEqualTo(DataBufferDecoder.class); @@ -103,6 +107,9 @@ public void defaultReaders() { assertThat(getNextDecoder(readers).getClass()).isEqualTo(ProtobufDecoder.class); // SPR-16804 assertThat(readers.get(this.index.getAndIncrement()).getClass()).isEqualTo(FormHttpMessageReader.class); + assertThat(readers.get(this.index.getAndIncrement()).getClass()).isEqualTo(DefaultPartHttpMessageReader.class); + assertThat(readers.get(this.index.getAndIncrement()).getClass()).isEqualTo(MultipartHttpMessageReader.class); + assertThat(readers.get(this.index.getAndIncrement()).getClass()).isEqualTo(PartEventHttpMessageReader.class); assertThat(getNextDecoder(readers).getClass()).isEqualTo(KotlinSerializationCborDecoder.class); assertThat(getNextDecoder(readers).getClass()).isEqualTo(KotlinSerializationJsonDecoder.class); assertThat(getNextDecoder(readers).getClass()).isEqualTo(KotlinSerializationProtobufDecoder.class); @@ -116,7 +123,7 @@ public void defaultReaders() { @Test public void defaultWriters() { List> writers = this.configurer.getWriters(); - assertThat(writers).hasSize(17); + assertThat(writers).hasSize(18); assertThat(getNextEncoder(writers).getClass()).isEqualTo(ByteArrayEncoder.class); assertThat(getNextEncoder(writers).getClass()).isEqualTo(ByteBufferEncoder.class); assertThat(getNextEncoder(writers).getClass()).isEqualTo(DataBufferEncoder.class); @@ -127,6 +134,7 @@ public void defaultWriters() { assertThat(writers.get(index.getAndIncrement()).getClass()).isEqualTo(ProtobufHttpMessageWriter.class); assertThat(writers.get(this.index.getAndIncrement()).getClass()).isEqualTo(MultipartHttpMessageWriter.class); assertThat(writers.get(this.index.getAndIncrement()).getClass()).isEqualTo(PartEventHttpMessageWriter.class); + assertThat(writers.get(this.index.getAndIncrement()).getClass()).isEqualTo(PartHttpMessageWriter.class); assertThat(getNextEncoder(writers).getClass()).isEqualTo(KotlinSerializationCborEncoder.class); assertThat(getNextEncoder(writers).getClass()).isEqualTo(KotlinSerializationJsonEncoder.class); assertThat(getNextEncoder(writers).getClass()).isEqualTo(KotlinSerializationProtobufEncoder.class); @@ -184,7 +192,7 @@ public void maxInMemorySize() { int size = 99; this.configurer.defaultCodecs().maxInMemorySize(size); List> readers = this.configurer.getReaders(); - assertThat(readers).hasSize(17); + assertThat(readers).hasSize(20); assertThat(((ByteArrayDecoder) getNextDecoder(readers)).getMaxInMemorySize()).isEqualTo(size); assertThat(((ByteBufferDecoder) getNextDecoder(readers)).getMaxInMemorySize()).isEqualTo(size); assertThat(((DataBufferDecoder) getNextDecoder(readers)).getMaxInMemorySize()).isEqualTo(size); @@ -194,7 +202,9 @@ public void maxInMemorySize() { assertThat(((StringDecoder) getNextDecoder(readers)).getMaxInMemorySize()).isEqualTo(size); assertThat(((ProtobufDecoder) getNextDecoder(readers)).getMaxMessageSize()).isEqualTo(size); assertThat(((FormHttpMessageReader) nextReader(readers)).getMaxInMemorySize()).isEqualTo(size); - + assertThat(((DefaultPartHttpMessageReader) nextReader(readers)).getMaxInMemorySize()).isEqualTo(size); + nextReader(readers); + assertThat(((PartEventHttpMessageReader) nextReader(readers)).getMaxInMemorySize()).isEqualTo(size); assertThat(((KotlinSerializationCborDecoder) getNextDecoder(readers)).getMaxInMemorySize()).isEqualTo(size); assertThat(((KotlinSerializationJsonDecoder) getNextDecoder(readers)).getMaxInMemorySize()).isEqualTo(size); assertThat(((KotlinSerializationProtobufDecoder) getNextDecoder(readers)).getMaxInMemorySize()).isEqualTo(size); @@ -245,7 +255,7 @@ public void clonedConfigurer() { writers = findCodec(this.configurer.getWriters(), MultipartHttpMessageWriter.class).getPartWriters(); assertThat(sseDecoder).isNotSameAs(jackson2Decoder); - assertThat(writers).hasSize(15); + assertThat(writers).hasSize(18); } @Test // gh-24194 @@ -255,7 +265,7 @@ public void cloneShouldNotDropMultipartCodecs() { List> writers = findCodec(clone.getWriters(), MultipartHttpMessageWriter.class).getPartWriters(); - assertThat(writers).hasSize(15); + assertThat(writers).hasSize(18); } @Test @@ -269,7 +279,7 @@ public void cloneShouldNotBeImpactedByChangesToOriginal() { List> writers = findCodec(clone.getWriters(), MultipartHttpMessageWriter.class).getPartWriters(); - assertThat(writers).hasSize(15); + assertThat(writers).hasSize(18); } private Decoder getNextDecoder(List> readers) { diff --git a/spring-web/src/test/java/org/springframework/http/codec/support/CodecConfigurerTests.java b/spring-web/src/test/java/org/springframework/http/codec/support/CodecConfigurerTests.java index fc3358514f1a..40b554afd1cd 100644 --- a/spring-web/src/test/java/org/springframework/http/codec/support/CodecConfigurerTests.java +++ b/spring-web/src/test/java/org/springframework/http/codec/support/CodecConfigurerTests.java @@ -58,6 +58,12 @@ import org.springframework.http.codec.json.Jackson2SmileEncoder; import org.springframework.http.codec.json.KotlinSerializationJsonDecoder; import org.springframework.http.codec.json.KotlinSerializationJsonEncoder; +import org.springframework.http.codec.multipart.DefaultPartHttpMessageReader; +import org.springframework.http.codec.multipart.MultipartHttpMessageReader; +import org.springframework.http.codec.multipart.MultipartHttpMessageWriter; +import org.springframework.http.codec.multipart.PartEventHttpMessageReader; +import org.springframework.http.codec.multipart.PartEventHttpMessageWriter; +import org.springframework.http.codec.multipart.PartHttpMessageWriter; import org.springframework.http.codec.protobuf.KotlinSerializationProtobufDecoder; import org.springframework.http.codec.protobuf.KotlinSerializationProtobufEncoder; import org.springframework.http.codec.protobuf.ProtobufDecoder; @@ -87,7 +93,7 @@ class CodecConfigurerTests { @Test void defaultReaders() { List> readers = this.configurer.getReaders(); - assertThat(readers).hasSize(16); + assertThat(readers).hasSize(19); assertThat(getNextDecoder(readers).getClass()).isEqualTo(ByteArrayDecoder.class); assertThat(getNextDecoder(readers).getClass()).isEqualTo(ByteBufferDecoder.class); assertThat(getNextDecoder(readers).getClass()).isEqualTo(DataBufferDecoder.class); @@ -97,6 +103,9 @@ void defaultReaders() { assertStringDecoder(getNextDecoder(readers), true); assertThat(getNextDecoder(readers).getClass()).isEqualTo(ProtobufDecoder.class); assertThat(readers.get(this.index.getAndIncrement()).getClass()).isEqualTo(FormHttpMessageReader.class); + assertThat(readers.get(this.index.getAndIncrement()).getClass()).isEqualTo(DefaultPartHttpMessageReader.class); + assertThat(readers.get(this.index.getAndIncrement()).getClass()).isEqualTo(MultipartHttpMessageReader.class); + assertThat(readers.get(this.index.getAndIncrement()).getClass()).isEqualTo(PartEventHttpMessageReader.class); assertThat(getNextDecoder(readers).getClass()).isEqualTo(KotlinSerializationCborDecoder.class); assertThat(getNextDecoder(readers).getClass()).isEqualTo(KotlinSerializationJsonDecoder.class); assertThat(getNextDecoder(readers).getClass()).isEqualTo(KotlinSerializationProtobufDecoder.class); @@ -109,7 +118,7 @@ void defaultReaders() { @Test void defaultWriters() { List> writers = this.configurer.getWriters(); - assertThat(writers).hasSize(15); + assertThat(writers).hasSize(18); assertThat(getNextEncoder(writers).getClass()).isEqualTo(ByteArrayEncoder.class); assertThat(getNextEncoder(writers).getClass()).isEqualTo(ByteBufferEncoder.class); assertThat(getNextEncoder(writers).getClass()).isEqualTo(DataBufferEncoder.class); @@ -118,6 +127,9 @@ void defaultWriters() { assertThat(writers.get(index.getAndIncrement()).getClass()).isEqualTo(ResourceHttpMessageWriter.class); assertStringEncoder(getNextEncoder(writers), true); assertThat(writers.get(index.getAndIncrement()).getClass()).isEqualTo(ProtobufHttpMessageWriter.class); + assertThat(writers.get(index.getAndIncrement()).getClass()).isEqualTo(MultipartHttpMessageWriter.class); + assertThat(writers.get(index.getAndIncrement()).getClass()).isEqualTo(PartEventHttpMessageWriter.class); + assertThat(writers.get(index.getAndIncrement()).getClass()).isEqualTo(PartHttpMessageWriter.class); assertThat(getNextEncoder(writers).getClass()).isEqualTo(KotlinSerializationCborEncoder.class); assertThat(getNextEncoder(writers).getClass()).isEqualTo(KotlinSerializationJsonEncoder.class); assertThat(getNextEncoder(writers).getClass()).isEqualTo(KotlinSerializationProtobufEncoder.class); @@ -149,7 +161,7 @@ void defaultAndCustomReaders() { List> readers = this.configurer.getReaders(); - assertThat(readers).hasSize(20); + assertThat(readers).hasSize(23); assertThat(getNextDecoder(readers)).isSameAs(customDecoder1); assertThat(readers.get(this.index.getAndIncrement())).isSameAs(customReader1); assertThat(getNextDecoder(readers).getClass()).isEqualTo(ByteArrayDecoder.class); @@ -161,6 +173,9 @@ void defaultAndCustomReaders() { assertThat(getNextDecoder(readers).getClass()).isEqualTo(StringDecoder.class); assertThat(getNextDecoder(readers).getClass()).isEqualTo(ProtobufDecoder.class); assertThat(readers.get(this.index.getAndIncrement()).getClass()).isEqualTo(FormHttpMessageReader.class); + assertThat(readers.get(this.index.getAndIncrement()).getClass()).isEqualTo(DefaultPartHttpMessageReader.class); + assertThat(readers.get(this.index.getAndIncrement()).getClass()).isEqualTo(MultipartHttpMessageReader.class); + assertThat(readers.get(this.index.getAndIncrement()).getClass()).isEqualTo(PartEventHttpMessageReader.class); assertThat(getNextDecoder(readers)).isSameAs(customDecoder2); assertThat(readers.get(this.index.getAndIncrement())).isSameAs(customReader2); assertThat(getNextDecoder(readers).getClass()).isEqualTo(KotlinSerializationCborDecoder.class); @@ -194,7 +209,7 @@ void defaultAndCustomWriters() { List> writers = this.configurer.getWriters(); - assertThat(writers).hasSize(19); + assertThat(writers).hasSize(22); assertThat(getNextEncoder(writers)).isSameAs(customEncoder1); assertThat(writers.get(this.index.getAndIncrement())).isSameAs(customWriter1); assertThat(getNextEncoder(writers).getClass()).isEqualTo(ByteArrayEncoder.class); @@ -205,6 +220,9 @@ void defaultAndCustomWriters() { assertThat(writers.get(index.getAndIncrement()).getClass()).isEqualTo(ResourceHttpMessageWriter.class); assertThat(getNextEncoder(writers).getClass()).isEqualTo(CharSequenceEncoder.class); assertThat(writers.get(index.getAndIncrement()).getClass()).isEqualTo(ProtobufHttpMessageWriter.class); + assertThat(writers.get(index.getAndIncrement()).getClass()).isEqualTo(MultipartHttpMessageWriter.class); + assertThat(writers.get(index.getAndIncrement()).getClass()).isEqualTo(PartEventHttpMessageWriter.class); + assertThat(writers.get(index.getAndIncrement()).getClass()).isEqualTo(PartHttpMessageWriter.class); assertThat(getNextEncoder(writers)).isSameAs(customEncoder2); assertThat(writers.get(this.index.getAndIncrement())).isSameAs(customWriter2); assertThat(getNextEncoder(writers).getClass()).isEqualTo(KotlinSerializationCborEncoder.class); diff --git a/spring-web/src/test/java/org/springframework/http/codec/support/ServerCodecConfigurerTests.java b/spring-web/src/test/java/org/springframework/http/codec/support/ServerCodecConfigurerTests.java index a7c84bf1382c..571001beb6ef 100644 --- a/spring-web/src/test/java/org/springframework/http/codec/support/ServerCodecConfigurerTests.java +++ b/spring-web/src/test/java/org/springframework/http/codec/support/ServerCodecConfigurerTests.java @@ -64,7 +64,9 @@ import org.springframework.http.codec.json.KotlinSerializationJsonEncoder; import org.springframework.http.codec.multipart.DefaultPartHttpMessageReader; import org.springframework.http.codec.multipart.MultipartHttpMessageReader; +import org.springframework.http.codec.multipart.MultipartHttpMessageWriter; import org.springframework.http.codec.multipart.PartEventHttpMessageReader; +import org.springframework.http.codec.multipart.PartEventHttpMessageWriter; import org.springframework.http.codec.multipart.PartHttpMessageWriter; import org.springframework.http.codec.protobuf.KotlinSerializationProtobufDecoder; import org.springframework.http.codec.protobuf.KotlinSerializationProtobufEncoder; @@ -117,7 +119,7 @@ public void defaultReaders() { @Test public void defaultWriters() { List> writers = this.configurer.getWriters(); - assertThat(writers).hasSize(17); + assertThat(writers).hasSize(19); assertThat(getNextEncoder(writers).getClass()).isEqualTo(ByteArrayEncoder.class); assertThat(getNextEncoder(writers).getClass()).isEqualTo(ByteBufferEncoder.class); assertThat(getNextEncoder(writers).getClass()).isEqualTo(DataBufferEncoder.class); @@ -126,6 +128,8 @@ public void defaultWriters() { assertThat(writers.get(index.getAndIncrement()).getClass()).isEqualTo(ResourceHttpMessageWriter.class); assertStringEncoder(getNextEncoder(writers), true); assertThat(writers.get(index.getAndIncrement()).getClass()).isEqualTo(ProtobufHttpMessageWriter.class); + assertThat(writers.get(this.index.getAndIncrement()).getClass()).isEqualTo(MultipartHttpMessageWriter.class); + assertThat(writers.get(this.index.getAndIncrement()).getClass()).isEqualTo(PartEventHttpMessageWriter.class); assertThat(writers.get(this.index.getAndIncrement()).getClass()).isEqualTo(PartHttpMessageWriter.class); assertThat(getNextEncoder(writers).getClass()).isEqualTo(KotlinSerializationCborEncoder.class); assertThat(getNextEncoder(writers).getClass()).isEqualTo(KotlinSerializationJsonEncoder.class); diff --git a/spring-webflux/src/test/java/org/springframework/web/reactive/config/WebFluxConfigurationSupportTests.java b/spring-webflux/src/test/java/org/springframework/web/reactive/config/WebFluxConfigurationSupportTests.java index fed7c90ee046..922ce42a1423 100644 --- a/spring-webflux/src/test/java/org/springframework/web/reactive/config/WebFluxConfigurationSupportTests.java +++ b/spring-webflux/src/test/java/org/springframework/web/reactive/config/WebFluxConfigurationSupportTests.java @@ -200,7 +200,7 @@ public void responseEntityResultHandler() { assertThat(handler.getOrder()).isEqualTo(0); List> writers = handler.getMessageWriters(); - assertThat(writers).hasSize(14); + assertThat(writers).hasSize(16); assertHasMessageWriter(writers, forClass(byte[].class), APPLICATION_OCTET_STREAM); assertHasMessageWriter(writers, forClass(ByteBuffer.class), APPLICATION_OCTET_STREAM); @@ -228,7 +228,7 @@ public void responseBodyResultHandler() { assertThat(handler.getOrder()).isEqualTo(100); List> writers = handler.getMessageWriters(); - assertThat(writers).hasSize(14); + assertThat(writers).hasSize(16); assertHasMessageWriter(writers, forClass(byte[].class), APPLICATION_OCTET_STREAM); assertHasMessageWriter(writers, forClass(ByteBuffer.class), APPLICATION_OCTET_STREAM);