From c79ae0c8422880586dbb52f0d18ca81f5e4ea4aa 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 070b0610ee..cfba0520ea 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 @@ public interface ClientCodecConfigurer extends CodecConfigurer { */ 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 @@ public interface ClientCodecConfigurer extends CodecConfigurer { 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 a27b0709a3..a5906a49b6 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 @@ public interface CodecConfigurer { * @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 @@ public interface CodecConfigurer { 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 c6f9222a19..767ab738e0 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 @@ public interface ServerCodecConfigurer extends CodecConfigurer { */ 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 777b5991ab..b4fca60dd9 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 class MultipartHttpMessageWriter extends MultipartWriterSupport 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 @@ public class MultipartHttpMessageWriter extends MultipartWriterSupport * @since 5.0.7 */ public List> getPartWriters() { - return Collections.unmodifiableList(this.partWriters); + return Collections.unmodifiableList(this.partWritersSupplier.get()); } @@ -264,8 +279,8 @@ public class MultipartHttpMessageWriter extends MultipartWriterSupport 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 4eed384a64..3b7b4ddb2e 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 9cfe0f3d7f..05c4c0f4a4 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.Collections; 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.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; @@ -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 @@ class BaseDefaultCodecs implements CodecConfigurer.DefaultCodecs, CodecConfigure 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 @@ class BaseDefaultCodecs implements CodecConfigurer.DefaultCodecs, CodecConfigure } } + @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 @@ class BaseDefaultCodecs implements CodecConfigurer.DefaultCodecs, CodecConfigure (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 @@ class BaseDefaultCodecs implements CodecConfigurer.DefaultCodecs, CodecConfigure 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 @@ class BaseDefaultCodecs implements CodecConfigurer.DefaultCodecs, CodecConfigure 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 c321682174..a494a74cce 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 @@ import org.springframework.lang.Nullable; */ 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 @@ class ClientDefaultCodecsImpl extends BaseDefaultCodecs implements ClientCodecCo 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 382d11bec8..fc03ca5de3 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 @@ public class DefaultClientCodecConfigurer extends BaseCodecConfigurer implements 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 186949655a..52c7bf4079 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 @@ package org.springframework.http.codec.support; 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 @@ import org.springframework.lang.Nullable; */ 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 e061ba55f2..aadb220fd0 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.Jackson2SmileDecoder; 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 class ClientCodecConfigurerTests { 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 class ClientCodecConfigurerTests { @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 class ClientCodecConfigurerTests { 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 class ClientCodecConfigurerTests { 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 class ClientCodecConfigurerTests { 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 class ClientCodecConfigurerTests { 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 class ClientCodecConfigurerTests { List> writers = findCodec(clone.getWriters(), MultipartHttpMessageWriter.class).getPartWriters(); - assertThat(writers).hasSize(15); + assertThat(writers).hasSize(18); } @Test @@ -269,7 +279,7 @@ public class ClientCodecConfigurerTests { 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 fc3358514f..40b554afd1 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.Jackson2SmileDecoder; 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 @@ class CodecConfigurerTests { 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 @@ class CodecConfigurerTests { @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 @@ class CodecConfigurerTests { 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 @@ class CodecConfigurerTests { 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 @@ class CodecConfigurerTests { 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 @@ class CodecConfigurerTests { 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 @@ class CodecConfigurerTests { 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 a7c84bf138..571001beb6 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.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; @@ -117,7 +119,7 @@ public class ServerCodecConfigurerTests { @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 class ServerCodecConfigurerTests { 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 fed7c90ee0..922ce42a14 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 class WebFluxConfigurationSupportTests { 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 class WebFluxConfigurationSupportTests { 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);