From 77c24aac2f448d02662f037ba9fd92b1d6c52b40 Mon Sep 17 00:00:00 2001 From: Arjen Poutsma Date: Wed, 10 Jul 2019 11:45:55 +0200 Subject: [PATCH] Remove DefaultMultipartMessageReader The DefaultMultipartMessageReader has been removed for 5.2 and will be part of a future release. This commit switches back to the SynchronossPartHttpMessageReader. gh-21659 --- .../http/codec/ServerCodecConfigurer.java | 12 - .../DefaultMultipartMessageReader.java | 375 ------------------ .../support/ServerDefaultCodecsImpl.java | 38 +- .../DefaultMultipartMessageReaderTests.java | 181 --------- .../support/ServerCodecConfigurerTests.java | 4 +- 5 files changed, 17 insertions(+), 593 deletions(-) delete mode 100644 spring-web/src/main/java/org/springframework/http/codec/multipart/DefaultMultipartMessageReader.java delete mode 100644 spring-web/src/test/java/org/springframework/http/codec/multipart/DefaultMultipartMessageReaderTests.java 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 ba8d19a3d4..fde144e3cb 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 @@ -17,7 +17,6 @@ package org.springframework.http.codec; import org.springframework.core.codec.Encoder; -import org.springframework.http.codec.multipart.Part; /** * Extension of {@link CodecConfigurer} for HTTP message reader and writer @@ -84,17 +83,6 @@ public interface ServerCodecConfigurer extends CodecConfigurer { * to customize the SSE encoder. */ void serverSentEventEncoder(Encoder encoder); - - /** - * Configure the {@code HttpMessageReader} to use for multipart messages - * (i.e. file uploads). - *

By default if this is not set, the - * {@link org.springframework.http.codec.multipart.DefaultMultipartMessageReader} is used. - * Use this method to customize the multipart reader, for instance to use - * {@link org.springframework.http.codec.multipart.SynchronossPartHttpMessageReader} - * instead. - */ - void multipartReader(HttpMessageReader multipartReader); } } diff --git a/spring-web/src/main/java/org/springframework/http/codec/multipart/DefaultMultipartMessageReader.java b/spring-web/src/main/java/org/springframework/http/codec/multipart/DefaultMultipartMessageReader.java deleted file mode 100644 index 95aa0fa30a..0000000000 --- a/spring-web/src/main/java/org/springframework/http/codec/multipart/DefaultMultipartMessageReader.java +++ /dev/null @@ -1,375 +0,0 @@ -/* - * Copyright 2002-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.http.codec.multipart; - -import java.nio.charset.Charset; -import java.nio.charset.StandardCharsets; -import java.nio.file.Path; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.concurrent.atomic.AtomicBoolean; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; - -import org.springframework.core.ResolvableType; -import org.springframework.core.codec.CodecException; -import org.springframework.core.io.buffer.DataBuffer; -import org.springframework.core.io.buffer.DataBufferUtils; -import org.springframework.core.io.buffer.PooledDataBuffer; -import org.springframework.http.ContentDisposition; -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpMessage; -import org.springframework.http.MediaType; -import org.springframework.http.ReactiveHttpInputMessage; -import org.springframework.http.codec.HttpMessageReader; -import org.springframework.http.codec.LoggingCodecSupport; -import org.springframework.lang.Nullable; -import org.springframework.util.Assert; -import org.springframework.util.StringUtils; - -/** - * {@code HttpMessageReader} for parsing {@code "multipart/form-data"} requests - * to a stream of {@link Part}'s. - * - *

This reader can be provided to {@link MultipartHttpMessageReader} in order - * to aggregate all parts into a Map. - * - * @author Arjen Poutsma - * @since 5.2 - * @see MultipartHttpMessageReader - */ -public class DefaultMultipartMessageReader extends LoggingCodecSupport implements HttpMessageReader { - - private static final Log logger = LogFactory.getLog(DefaultMultipartMessageReader.class); - - private static final byte CR = '\r'; - - private static final byte LF = '\n'; - - private static final byte HYPHEN = '-'; - - private static final byte[] FIRST_BOUNDARY_PREFIX = {HYPHEN, HYPHEN}; - - private static final byte[] BOUNDARY_PREFIX = {CR, LF, HYPHEN, HYPHEN}; - - private static final byte[] HEADER_BODY_SEPARATOR = {CR, LF, CR, LF}; - - private static final String HEADER_SEPARATOR = "\\r\\n"; - - private static final DataBufferUtils.Matcher HEADER_MATCHER = DataBufferUtils.matcher(HEADER_BODY_SEPARATOR); - - - @Override - public List getReadableMediaTypes() { - return Collections.singletonList(MediaType.MULTIPART_FORM_DATA); - } - - @Override - public boolean canRead(ResolvableType elementType, @Nullable MediaType mediaType) { - return (Part.class.equals(elementType.toClass()) && - (mediaType == null || MediaType.MULTIPART_FORM_DATA.isCompatibleWith(mediaType))); - } - - @Override - public Flux read(ResolvableType elementType, ReactiveHttpInputMessage message, Map hints) { - byte[] boundary = boundary(message); - if (boundary == null) { - return Flux.error(new CodecException("No multipart boundary found in Content-Type: \"" + - message.getHeaders().getContentType() + "\"")); - } - byte[] boundaryNeedle = concat(BOUNDARY_PREFIX, boundary); - Flux body = skipUntilFirstBoundary(message.getBody(), boundary); - - return DataBufferUtils.split(body, boundaryNeedle) - .takeWhile(DefaultMultipartMessageReader::notLastBoundary) - .map(DefaultMultipartMessageReader::toPart) - .doOnDiscard(PooledDataBuffer.class, DataBufferUtils::release) - .doOnDiscard(DefaultPart.class, part -> DataBufferUtils.release(part.body)); - } - - @Nullable - private static byte[] boundary(HttpMessage message) { - MediaType contentType = message.getHeaders().getContentType(); - if (contentType != null) { - String boundary = contentType.getParameter("boundary"); - if (boundary != null) { - return boundary.getBytes(StandardCharsets.ISO_8859_1); - } - } - return null; - } - - /** - * Finds the fist occurrence of the boundary in the given stream of data buffers, and skips - * all data until then. Note that the first boundary of a multipart message does not contain - * the initial \r\n, hence the need for a special boundary matcher. - */ - private static Flux skipUntilFirstBoundary(Flux dataBuffers, byte[] boundary) { - byte[] needle = concat(FIRST_BOUNDARY_PREFIX, boundary); - DataBufferUtils.Matcher matcher = DataBufferUtils.matcher(needle); - AtomicBoolean found = new AtomicBoolean(); - - return dataBuffers.concatMap(dataBuffer -> { - if (found.get()) { - return Mono.just(dataBuffer); - } - else { - int endIdx = matcher.match(dataBuffer); - if (endIdx != -1) { - found.set(true); - int length = dataBuffer.writePosition() - 1 - endIdx; - DataBuffer slice = dataBuffer.retainedSlice(endIdx + 1, length); - DataBufferUtils.release(dataBuffer); - if (logger.isTraceEnabled()) { - logger.trace( - "Found last byte of first boundary (" + toString(boundary) - + ") at " + endIdx); - } - return Mono.just(slice); - } - else { - DataBufferUtils.release(dataBuffer); - return Mono.empty(); - } - } - }); - } - - /** - * Indicates whether the given data buffer is not the last boundary, i.e. it does not start - * with two hyphens. - */ - private static boolean notLastBoundary(DataBuffer dataBuffer) { - if (dataBuffer.readableByteCount() >= 2) { - int readPosition = dataBuffer.readPosition(); - if (dataBuffer.getByte(readPosition) == HYPHEN && dataBuffer.getByte(readPosition + 1) == HYPHEN) { - DataBufferUtils.release(dataBuffer); - return false; - } - } - return true; - } - - /** - * Convert the given data buffer into a Part. All data up until the header separator (\r\n\r\n) - * is passed to {@link #toHeaders(DataBuffer)}, the remaining data is considered to be the - * body. - */ - private static Part toPart(DataBuffer dataBuffer) { - int readPosition = dataBuffer.readPosition(); - if (dataBuffer.readableByteCount() >= 2) { - if (dataBuffer.getByte(readPosition) == CR && dataBuffer.getByte(readPosition + 1) == LF) { - dataBuffer.readPosition(readPosition + 2); - } - } - - int endIdx = HEADER_MATCHER.match(dataBuffer); - - HttpHeaders headers; - DataBuffer body; - if (endIdx > 0) { - if (logger.isTraceEnabled()) { - logger.trace("Found last byte of part header at " + endIdx ); - } - readPosition = dataBuffer.readPosition(); - int headersLength = endIdx + 1 - (readPosition + HEADER_BODY_SEPARATOR.length); - DataBuffer headersBuffer = dataBuffer.retainedSlice(readPosition, headersLength); - int bodyLength = dataBuffer.writePosition() - (1 + endIdx); - body = dataBuffer.retainedSlice(endIdx + 1, bodyLength); - headers = toHeaders(headersBuffer); - } - else { - if (logger.isTraceEnabled()) { - logger.trace("No header found"); - } - headers = new HttpHeaders(); - body = DataBufferUtils.retain(dataBuffer); - } - DataBufferUtils.release(dataBuffer); - - ContentDisposition cd = headers.getContentDisposition(); - MediaType contentType = headers.getContentType(); - if (StringUtils.hasLength(cd.getFilename())) { - return new DefaultFilePart(headers, body); - } - else if (StringUtils.hasLength(cd.getName()) && - (contentType == null || MediaType.TEXT_PLAIN.isCompatibleWith(contentType))) { - return new DefaultFormPart(headers, body); - } - else { - return new DefaultPart(headers, body); - } - } - - /** - * Convert the given data buffer into a {@link HttpHeaders} instance. The given string is read - * as US-ASCII, then split along \r\n line boundaries, each line containing a header name and - * value(s). - */ - private static HttpHeaders toHeaders(DataBuffer dataBuffer) { - byte[] bytes = new byte[dataBuffer.readableByteCount()]; - dataBuffer.read(bytes); - DataBufferUtils.release(dataBuffer); - String string = new String(bytes, StandardCharsets.US_ASCII); - String[] lines = string.split(HEADER_SEPARATOR); - HttpHeaders result = new HttpHeaders(); - for (String line : lines) { - int idx = line.indexOf(':'); - if (idx != -1) { - String name = line.substring(0, idx); - String value = line.substring(idx + 1); - while (value.startsWith(" ")) { - value = value.substring(1); - } - String[] tokens = StringUtils.tokenizeToStringArray(value, ","); - for (String token : tokens) { - result.add(name, token); - } - } - } - return result; - } - - private static String toString(byte[] bytes) { - StringBuilder builder = new StringBuilder(); - for (byte b : bytes) { - if (b == CR) { - builder.append("␍"); - } - else if (b == LF) { - builder.append("␤"); - } - else if (b >= 20 && b <= 126) { - builder.append((char) b); - } - } - return builder.toString(); - } - - - @Override - public Mono readMono(ResolvableType elementType, ReactiveHttpInputMessage message, - Map hints) { - return Mono.error(new UnsupportedOperationException( - "Cannot read multipart request body into single Part")); - } - - private static byte[] concat(byte[]... byteArrays) { - int length = 0; - for (byte[] byteArray : byteArrays) { - length += byteArray.length; - } - byte[] result = new byte[length]; - length = 0; - for (byte[] byteArray : byteArrays) { - System.arraycopy(byteArray, 0, result, length, byteArray.length); - length += byteArray.length; - } - return result; - } - - - private static class DefaultPart implements Part { - - private final HttpHeaders headers; - - protected final DataBuffer body; - - public DefaultPart(HttpHeaders headers, DataBuffer body) { - this.headers = headers; - this.body = body; - } - - @Override - public String name() { - String name = headers().getContentDisposition().getName(); - Assert.state(name != null, "No name available"); - return name; - } - - @Override - public HttpHeaders headers() { - return this.headers; - } - - @Override - public Flux content() { - return Flux.just(this.body); - } - } - - - private static class DefaultFormPart extends DefaultPart implements FormFieldPart { - - private String value; - - public DefaultFormPart(HttpHeaders headers, DataBuffer body) { - super(headers, body); - this.value = toString(body, contentTypeCharset(headers)); - } - - private static String toString(DataBuffer dataBuffer, Charset charset) { - byte[] bytes = new byte[dataBuffer.readableByteCount()]; - dataBuffer.read(bytes); - DataBufferUtils.release(dataBuffer); - return new String(bytes, charset).trim(); - } - - private static Charset contentTypeCharset(HttpHeaders headers) { - MediaType contentType = headers.getContentType(); - if (contentType != null) { - Charset charset = contentType.getCharset(); - if (charset != null) { - return charset; - } - } - return StandardCharsets.ISO_8859_1; - } - - @Override - public String value() { - return this.value; - } - - } - - - private static class DefaultFilePart extends DefaultPart implements FilePart { - - public DefaultFilePart(HttpHeaders headers, DataBuffer body) { - super(headers, body); - } - - @Override - public String filename() { - String filename = headers().getContentDisposition().getFilename(); - Assert.state(filename != null, "No filename available"); - return filename; - } - - @Override - public Mono transferTo(Path dest) { - return DataBufferUtils.write(content(), dest); - } - - } - -} 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 4529cd5f5b..ef91fb7c36 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 @@ -20,13 +20,12 @@ 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.LoggingCodecSupport; import org.springframework.http.codec.ServerCodecConfigurer; import org.springframework.http.codec.ServerSentEventHttpMessageWriter; -import org.springframework.http.codec.multipart.DefaultMultipartMessageReader; import org.springframework.http.codec.multipart.MultipartHttpMessageReader; -import org.springframework.http.codec.multipart.Part; +import org.springframework.http.codec.multipart.SynchronossPartHttpMessageReader; import org.springframework.lang.Nullable; +import org.springframework.util.ClassUtils; /** * Default implementation of {@link ServerCodecConfigurer.ServerDefaultCodecs}. @@ -35,41 +34,34 @@ import org.springframework.lang.Nullable; */ class ServerDefaultCodecsImpl extends BaseDefaultCodecs implements ServerCodecConfigurer.ServerDefaultCodecs { + private static final boolean synchronossMultipartPresent = + ClassUtils.isPresent("org.synchronoss.cloud.nio.multipart.NioMultipartParser", + DefaultServerCodecConfigurer.class.getClassLoader()); + + @Nullable private Encoder sseEncoder; - @Nullable - private HttpMessageReader multipartReader; @Override public void serverSentEventEncoder(Encoder encoder) { this.sseEncoder = encoder; } - @Override - public void multipartReader(HttpMessageReader multipartReader) { - this.multipartReader = multipartReader; - } - @Override protected void extendTypedReaders(List> typedReaders) { + if (synchronossMultipartPresent) { + boolean enable = isEnableLoggingRequestDetails(); - HttpMessageReader partReader = getMultipartReader(); + SynchronossPartHttpMessageReader partReader = new SynchronossPartHttpMessageReader(); + partReader.setEnableLoggingRequestDetails(enable); + typedReaders.add(partReader); - boolean logRequestDetails = isEnableLoggingRequestDetails(); - if (partReader instanceof LoggingCodecSupport) { - ((LoggingCodecSupport) partReader).setEnableLoggingRequestDetails(logRequestDetails); + MultipartHttpMessageReader reader = new MultipartHttpMessageReader(partReader); + reader.setEnableLoggingRequestDetails(enable); + typedReaders.add(reader); } - typedReaders.add(partReader); - - MultipartHttpMessageReader reader = new MultipartHttpMessageReader(partReader); - reader.setEnableLoggingRequestDetails(logRequestDetails); - typedReaders.add(reader); - } - - private HttpMessageReader getMultipartReader() { - return this.multipartReader != null ? this.multipartReader : new DefaultMultipartMessageReader(); } @Override diff --git a/spring-web/src/test/java/org/springframework/http/codec/multipart/DefaultMultipartMessageReaderTests.java b/spring-web/src/test/java/org/springframework/http/codec/multipart/DefaultMultipartMessageReaderTests.java deleted file mode 100644 index 963ac36502..0000000000 --- a/spring-web/src/test/java/org/springframework/http/codec/multipart/DefaultMultipartMessageReaderTests.java +++ /dev/null @@ -1,181 +0,0 @@ -/* - * Copyright 2002-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.http.codec.multipart; - -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.concurrent.CountDownLatch; - -import org.junit.Test; -import reactor.core.publisher.Flux; -import reactor.test.StepVerifier; - -import org.springframework.core.io.ClassPathResource; -import org.springframework.core.io.Resource; -import org.springframework.core.io.buffer.AbstractDataBufferAllocatingTestCase; -import org.springframework.core.io.buffer.DataBuffer; -import org.springframework.core.io.buffer.DataBufferUtils; -import org.springframework.http.MediaType; -import org.springframework.mock.http.server.reactive.test.MockServerHttpRequest; - -import static java.util.Collections.emptyMap; -import static java.util.Collections.singletonMap; -import static org.assertj.core.api.Assertions.assertThat; -import static org.springframework.core.ResolvableType.forClass; - -/** - * @author Arjen Poutsma - */ -public class DefaultMultipartMessageReaderTests extends AbstractDataBufferAllocatingTestCase { - - private static final String LOREM_IPSUM = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer iaculis metus id vestibulum nullam."; - - private static final String MUSPI_MEROL = new StringBuilder(LOREM_IPSUM).reverse().toString(); - - private static final int BUFFER_SIZE = 16; - - private final DefaultMultipartMessageReader reader = new DefaultMultipartMessageReader(); - - @Test - public void canRead() { - assertThat(this.reader.canRead(forClass(Part.class), MediaType.MULTIPART_FORM_DATA)).isTrue(); - } - - @Test - public void partNoHeader() { - MockServerHttpRequest request = createRequest( - new ClassPathResource("part-no-header.multipart", getClass()), "boundary"); - - Flux result = this.reader.read(forClass(Part.class), request, emptyMap()); - - StepVerifier.create(result) - .consumeNextWith(part -> { - assertThat(part.headers().isEmpty()).isTrue(); - part.content().subscribe(DataBufferUtils::release); - }) - .verifyComplete(); - } - - @Test - public void partNoEndBoundary() { - MockServerHttpRequest request = createRequest( - new ClassPathResource("part-no-end-boundary.multipart", getClass()), "boundary"); - - Flux result = this.reader.read(forClass(Part.class), request, emptyMap()); - - StepVerifier.create(result) - .consumeNextWith(part -> - part.content().subscribe(DataBufferUtils::release) - ) - .verifyComplete(); - } - - @Test - public void firefox() { - testBrowser(new ClassPathResource("firefox.multipart", getClass()), - "---------------------------18399284482060392383840973206"); - } - - @Test - public void chrome() { - testBrowser(new ClassPathResource("chrome.multipart", getClass()), - "----WebKitFormBoundaryEveBLvRT65n21fwU"); - } - - @Test - public void safari() { - testBrowser(new ClassPathResource("safari.multipart", getClass()), - "----WebKitFormBoundaryG8fJ50opQOML0oGD"); - } - - private void testBrowser(Resource resource, String boundary) { - MockServerHttpRequest request = createRequest(resource, boundary); - - Flux result = this.reader.read(forClass(Part.class), request, emptyMap()); - - StepVerifier.create(result) - .consumeNextWith(part -> testBrowserFormField(part, "text1", "a")) - .consumeNextWith(part -> testBrowserFormField(part, "text2", "b")) - .consumeNextWith(part -> testBrowserFile(part, "file1", "a.txt", LOREM_IPSUM)) - .consumeNextWith(part -> testBrowserFile(part, "file2", "a.txt", LOREM_IPSUM)) - .consumeNextWith(part -> testBrowserFile(part, "file2", "b.txt", MUSPI_MEROL)) - .verifyComplete(); - } - - private MockServerHttpRequest createRequest(Resource resource, String boundary) { - Flux body = DataBufferUtils - .readByteChannel(resource::readableChannel, this.bufferFactory, BUFFER_SIZE); - - MediaType contentType = new MediaType("multipart", "form-data", singletonMap("boundary", boundary)); - return MockServerHttpRequest.post("/") - .contentType(contentType) - .body(body); - } - - private static void testBrowserFormField(Part part, String name, String value) { - boolean condition = part instanceof FormFieldPart; - assertThat(condition).isTrue(); - assertThat(part.name()).isEqualTo(name); - FormFieldPart formField = (FormFieldPart) part; - assertThat(formField.value()).isEqualTo(value); - } - - private static void testBrowserFile(Part part, String name, String filename, String contents) { - try { - boolean condition = part instanceof FilePart; - assertThat(condition).isTrue(); - assertThat(part.name()).isEqualTo(name); - FilePart file = (FilePart) part; - assertThat(file.filename()).isEqualTo(filename); - - Path tempFile = Files.createTempFile("DefaultMultipartMessageReaderTests", null); - - CountDownLatch latch = new CountDownLatch(1); - file.transferTo(tempFile) - .subscribe(null, - throwable -> { - throw new AssertionError(throwable.getMessage(), throwable); - }, - () -> { - try { - verifyContents(tempFile, contents); - } - finally { - latch.countDown(); - } - - }); - - latch.await(); - } - catch (Exception ex) { - throw new AssertionError(ex); - } - } - - private static void verifyContents(Path tempFile, String contents) { - try { - String result = String.join("", Files.readAllLines(tempFile)); - assertThat(result).isEqualTo(contents); - } - catch (IOException ex) { - throw new AssertionError(ex); - } - } - -} 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 1f8116e077..2e9851d7aa 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 @@ -52,8 +52,8 @@ import org.springframework.http.codec.json.Jackson2JsonDecoder; import org.springframework.http.codec.json.Jackson2JsonEncoder; import org.springframework.http.codec.json.Jackson2SmileDecoder; import org.springframework.http.codec.json.Jackson2SmileEncoder; -import org.springframework.http.codec.multipart.DefaultMultipartMessageReader; import org.springframework.http.codec.multipart.MultipartHttpMessageReader; +import org.springframework.http.codec.multipart.SynchronossPartHttpMessageReader; import org.springframework.http.codec.protobuf.ProtobufDecoder; import org.springframework.http.codec.protobuf.ProtobufHttpMessageWriter; import org.springframework.http.codec.xml.Jaxb2XmlDecoder; @@ -86,7 +86,7 @@ public class ServerCodecConfigurerTests { 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(DefaultMultipartMessageReader.class); + assertThat(readers.get(this.index.getAndIncrement()).getClass()).isEqualTo(SynchronossPartHttpMessageReader.class); assertThat(readers.get(this.index.getAndIncrement()).getClass()).isEqualTo(MultipartHttpMessageReader.class); assertThat(getNextDecoder(readers).getClass()).isEqualTo(Jackson2JsonDecoder.class); assertThat(getNextDecoder(readers).getClass()).isEqualTo(Jackson2SmileDecoder.class);