From ed2a3bd451ca8a330bb1ef7fb5e9f06a2fb5793c Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Wed, 15 May 2019 17:31:59 -0400 Subject: [PATCH] Take advantage of Encoder#encodeValue EncoderHttpMessageWriter takes advantage of the Encoder#encodeValue that's new in 5.2 in order to produce a Mono instead of producing a Flux and then using flux.singleOrEmpty(). Closes gh-22952 --- .../http/codec/EncoderHttpMessageWriter.java | 20 +++---- .../util/pattern/PathPatternRouteMatcher.java | 2 +- .../codec/EncoderHttpMessageWriterTests.java | 59 ++++++++++++------- 3 files changed, 50 insertions(+), 31 deletions(-) diff --git a/spring-web/src/main/java/org/springframework/http/codec/EncoderHttpMessageWriter.java b/spring-web/src/main/java/org/springframework/http/codec/EncoderHttpMessageWriter.java index af0dde1289e..bd6c98ba640 100644 --- a/spring-web/src/main/java/org/springframework/http/codec/EncoderHttpMessageWriter.java +++ b/spring-web/src/main/java/org/springframework/http/codec/EncoderHttpMessageWriter.java @@ -29,8 +29,8 @@ import org.springframework.core.codec.AbstractEncoder; import org.springframework.core.codec.Encoder; import org.springframework.core.codec.Hints; import org.springframework.core.io.buffer.DataBuffer; +import org.springframework.core.io.buffer.DataBufferFactory; import org.springframework.core.io.buffer.PooledDataBuffer; -import org.springframework.http.HttpHeaders; import org.springframework.http.HttpLogging; import org.springframework.http.MediaType; import org.springframework.http.ReactiveHttpOutputMessage; @@ -114,24 +114,24 @@ public class EncoderHttpMessageWriter implements HttpMessageWriter { MediaType contentType = updateContentType(message, mediaType); - Flux body = this.encoder.encode( - inputStream, message.bufferFactory(), elementType, contentType, hints); - if (inputStream instanceof Mono) { - HttpHeaders headers = message.getHeaders(); - return body - .singleOrEmpty() + return Mono.from(inputStream) .switchIfEmpty(Mono.defer(() -> { - headers.setContentLength(0); + message.getHeaders().setContentLength(0); return message.setComplete().then(Mono.empty()); })) - .flatMap(buffer -> { - headers.setContentLength(buffer.readableByteCount()); + .flatMap(value -> { + DataBufferFactory factory = message.bufferFactory(); + DataBuffer buffer = this.encoder.encodeValue(value, factory, elementType, contentType, hints); + message.getHeaders().setContentLength(buffer.readableByteCount()); return message.writeWith(Mono.just(buffer) .doOnDiscard(PooledDataBuffer.class, PooledDataBuffer::release)); }); } + Flux body = this.encoder.encode( + inputStream, message.bufferFactory(), elementType, contentType, hints); + if (isStreamingMediaType(contentType)) { return message.writeAndFlushWith(body.map(buffer -> Mono.just(buffer).doOnDiscard(PooledDataBuffer.class, PooledDataBuffer::release))); diff --git a/spring-web/src/main/java/org/springframework/web/util/pattern/PathPatternRouteMatcher.java b/spring-web/src/main/java/org/springframework/web/util/pattern/PathPatternRouteMatcher.java index 6c5c2cc1726..4dad70eb4f5 100644 --- a/spring-web/src/main/java/org/springframework/web/util/pattern/PathPatternRouteMatcher.java +++ b/spring-web/src/main/java/org/springframework/web/util/pattern/PathPatternRouteMatcher.java @@ -103,4 +103,4 @@ public class PathPatternRouteMatcher implements RouteMatcher { } } -} \ No newline at end of file +} diff --git a/spring-web/src/test/java/org/springframework/http/codec/EncoderHttpMessageWriterTests.java b/spring-web/src/test/java/org/springframework/http/codec/EncoderHttpMessageWriterTests.java index 1f09ff8d33f..1d3cd879cbc 100644 --- a/spring-web/src/test/java/org/springframework/http/codec/EncoderHttpMessageWriterTests.java +++ b/spring-web/src/test/java/org/springframework/http/codec/EncoderHttpMessageWriterTests.java @@ -84,13 +84,15 @@ public class EncoderHttpMessageWriterTests { @Test public void getWritableMediaTypes() { - HttpMessageWriter writer = getWriter(MimeTypeUtils.TEXT_HTML, MimeTypeUtils.TEXT_XML); + configureEncoder(MimeTypeUtils.TEXT_HTML, MimeTypeUtils.TEXT_XML); + HttpMessageWriter writer = new EncoderHttpMessageWriter<>(this.encoder); assertEquals(Arrays.asList(TEXT_HTML, TEXT_XML), writer.getWritableMediaTypes()); } @Test public void canWrite() { - HttpMessageWriter writer = getWriter(MimeTypeUtils.TEXT_HTML); + configureEncoder(MimeTypeUtils.TEXT_HTML); + HttpMessageWriter writer = new EncoderHttpMessageWriter<>(this.encoder); given(this.encoder.canEncode(forClass(String.class), TEXT_HTML)).willReturn(true); assertTrue(writer.canWrite(forClass(String.class), TEXT_HTML)); @@ -99,8 +101,9 @@ public class EncoderHttpMessageWriterTests { @Test public void useNegotiatedMediaType() { - HttpMessageWriter writer = getWriter(MimeTypeUtils.ALL); - writer.write(Mono.just("body"), forClass(String.class), TEXT_PLAIN, this.response, NO_HINTS); + configureEncoder(TEXT_PLAIN); + HttpMessageWriter writer = new EncoderHttpMessageWriter<>(this.encoder); + writer.write(Flux.empty(), forClass(String.class), TEXT_PLAIN, this.response, NO_HINTS); assertEquals(TEXT_PLAIN, response.getHeaders().getContentType()); assertEquals(TEXT_PLAIN, this.mediaTypeCaptor.getValue()); @@ -119,8 +122,9 @@ public class EncoderHttpMessageWriterTests { this.mediaTypeCaptor = ArgumentCaptor.forClass(MediaType.class); MimeType defaultContentType = MimeTypeUtils.TEXT_XML; - HttpMessageWriter writer = getWriter(defaultContentType); - writer.write(Mono.just("body"), forClass(String.class), negotiatedMediaType, this.response, NO_HINTS); + configureEncoder(defaultContentType); + HttpMessageWriter writer = new EncoderHttpMessageWriter<>(this.encoder); + writer.write(Flux.empty(), forClass(String.class), negotiatedMediaType, this.response, NO_HINTS); assertEquals(defaultContentType, this.response.getHeaders().getContentType()); assertEquals(defaultContentType, this.mediaTypeCaptor.getValue()); @@ -128,8 +132,9 @@ public class EncoderHttpMessageWriterTests { @Test public void useDefaultMediaTypeCharset() { - HttpMessageWriter writer = getWriter(TEXT_PLAIN_UTF_8, TEXT_HTML); - writer.write(Mono.just("body"), forClass(String.class), TEXT_HTML, response, NO_HINTS); + configureEncoder(TEXT_PLAIN_UTF_8, TEXT_HTML); + HttpMessageWriter writer = new EncoderHttpMessageWriter<>(this.encoder); + writer.write(Flux.empty(), forClass(String.class), TEXT_HTML, response, NO_HINTS); assertEquals(new MediaType("text", "html", UTF_8), this.response.getHeaders().getContentType()); assertEquals(new MediaType("text", "html", UTF_8), this.mediaTypeCaptor.getValue()); @@ -138,8 +143,9 @@ public class EncoderHttpMessageWriterTests { @Test public void useNegotiatedMediaTypeCharset() { MediaType negotiatedMediaType = new MediaType("text", "html", ISO_8859_1); - HttpMessageWriter writer = getWriter(TEXT_PLAIN_UTF_8, TEXT_HTML); - writer.write(Mono.just("body"), forClass(String.class), negotiatedMediaType, this.response, NO_HINTS); + configureEncoder(TEXT_PLAIN_UTF_8, TEXT_HTML); + HttpMessageWriter writer = new EncoderHttpMessageWriter<>(this.encoder); + writer.write(Flux.empty(), forClass(String.class), negotiatedMediaType, this.response, NO_HINTS); assertEquals(negotiatedMediaType, this.response.getHeaders().getContentType()); assertEquals(negotiatedMediaType, this.mediaTypeCaptor.getValue()); @@ -150,8 +156,9 @@ public class EncoderHttpMessageWriterTests { MediaType outputMessageMediaType = MediaType.TEXT_HTML; this.response.getHeaders().setContentType(outputMessageMediaType); - HttpMessageWriter writer = getWriter(TEXT_PLAIN_UTF_8, TEXT_HTML); - writer.write(Mono.just("body"), forClass(String.class), TEXT_PLAIN, this.response, NO_HINTS); + configureEncoder(TEXT_PLAIN_UTF_8, TEXT_HTML); + HttpMessageWriter writer = new EncoderHttpMessageWriter<>(this.encoder); + writer.write(Flux.empty(), forClass(String.class), TEXT_PLAIN, this.response, NO_HINTS); assertEquals(outputMessageMediaType, this.response.getHeaders().getContentType()); assertEquals(outputMessageMediaType, this.mediaTypeCaptor.getValue()); @@ -161,7 +168,8 @@ public class EncoderHttpMessageWriterTests { public void setContentLengthForMonoBody() { DefaultDataBufferFactory factory = new DefaultDataBufferFactory(); DataBuffer buffer = factory.wrap("body".getBytes(StandardCharsets.UTF_8)); - HttpMessageWriter writer = getWriter(Flux.just(buffer), MimeTypeUtils.TEXT_PLAIN); + configureEncoder(buffer, MimeTypeUtils.TEXT_PLAIN); + HttpMessageWriter writer = new EncoderHttpMessageWriter<>(this.encoder); writer.write(Mono.just("body"), forClass(String.class), TEXT_PLAIN, this.response, NO_HINTS).block(); assertEquals(4, this.response.getHeaders().getContentLength()); @@ -180,7 +188,8 @@ public class EncoderHttpMessageWriterTests { @Test // SPR-17220 public void emptyBodyWritten() { - HttpMessageWriter writer = getWriter(MimeTypeUtils.TEXT_PLAIN); + configureEncoder(MimeTypeUtils.TEXT_PLAIN); + HttpMessageWriter writer = new EncoderHttpMessageWriter<>(this.encoder); writer.write(Mono.empty(), forClass(String.class), TEXT_PLAIN, this.response, NO_HINTS).block(); StepVerifier.create(this.response.getBody()).expectComplete(); assertEquals(0, this.response.getHeaders().getContentLength()); @@ -188,25 +197,35 @@ public class EncoderHttpMessageWriterTests { @Test // gh-22936 public void isStreamingMediaType() throws InvocationTargetException, IllegalAccessException { - HttpMessageWriter writer = getWriter(TEXT_HTML); + configureEncoder(TEXT_HTML); MediaType streamingMediaType = new MediaType(TEXT_PLAIN, Collections.singletonMap("streaming", "true")); given(this.encoder.getStreamingMediaTypes()).willReturn(Arrays.asList(streamingMediaType)); + + HttpMessageWriter writer = new EncoderHttpMessageWriter<>(this.encoder); Method method = ReflectionUtils.findMethod(writer.getClass(), "isStreamingMediaType", MediaType.class); ReflectionUtils.makeAccessible(method); + assertTrue((Boolean) method.invoke(writer, streamingMediaType)); assertFalse((Boolean) method.invoke(writer, new MediaType(TEXT_PLAIN, Collections.singletonMap("streaming", "false")))); assertFalse((Boolean) method.invoke(writer, TEXT_HTML)); } - private HttpMessageWriter getWriter(MimeType... mimeTypes) { - return getWriter(Flux.empty(), mimeTypes); + private void configureEncoder(MimeType... mimeTypes) { + configureEncoder(Flux.empty(), mimeTypes); + } + + private void configureEncoder(Flux encodedStream, MimeType... mimeTypes) { + List typeList = Arrays.asList(mimeTypes); + given(this.encoder.getEncodableMimeTypes()).willReturn(typeList); + given(this.encoder.encode(any(), any(), any(), this.mediaTypeCaptor.capture(), any())) + .willReturn(encodedStream); } - private HttpMessageWriter getWriter(Flux encodedStream, MimeType... mimeTypes) { + private void configureEncoder(DataBuffer dataBuffer, MimeType... mimeTypes) { List typeList = Arrays.asList(mimeTypes); given(this.encoder.getEncodableMimeTypes()).willReturn(typeList); - given(this.encoder.encode(any(), any(), any(), this.mediaTypeCaptor.capture(), any())).willReturn(encodedStream); - return new EncoderHttpMessageWriter<>(this.encoder); + given(this.encoder.encodeValue(any(), any(), any(), this.mediaTypeCaptor.capture(), any())) + .willReturn(dataBuffer); } }