From c8d49ed284a2b2ec0557445d36e13e7bf881ffc3 Mon Sep 17 00:00:00 2001 From: Sebastien Deleuze Date: Thu, 9 May 2019 14:54:43 +0200 Subject: [PATCH] Fix EncoderHttpMessageWriter.isStreamingMediaType() Closes gh-22936 --- .../http/codec/EncoderHttpMessageWriter.java | 21 ++++++++++++++----- .../codec/EncoderHttpMessageWriterTests.java | 19 ++++++++++++++--- 2 files changed, 32 insertions(+), 8 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 9395cd29ff..ce4d481429 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 @@ -38,6 +38,7 @@ import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.http.server.reactive.ServerHttpResponse; import org.springframework.lang.Nullable; import org.springframework.util.Assert; +import org.springframework.util.StringUtils; /** * {@code HttpMessageWriter} that wraps and delegates to an {@link Encoder}. @@ -166,19 +167,29 @@ public class EncoderHttpMessageWriter implements HttpMessageWriter { return main; } - private boolean isStreamingMediaType(@Nullable MediaType contentType) { - if (contentType == null || !(this.encoder instanceof HttpMessageEncoder)) { + private boolean isStreamingMediaType(@Nullable MediaType mediaType) { + if (mediaType == null || !(this.encoder instanceof HttpMessageEncoder)) { return false; } - for (MediaType mediaType : ((HttpMessageEncoder) this.encoder).getStreamingMediaTypes()) { - if (contentType.isCompatibleWith(mediaType) && - contentType.getParameters().keySet().containsAll(mediaType.getParameters().keySet())) { + for (MediaType streamingMediaType : ((HttpMessageEncoder) this.encoder).getStreamingMediaTypes()) { + if (mediaType.isCompatibleWith(streamingMediaType) && matchParameters(mediaType, streamingMediaType)) { return true; } } return false; } + private boolean matchParameters(MediaType streamingMediaType, MediaType mediaType) { + for (String name : streamingMediaType.getParameters().keySet()) { + String s1 = streamingMediaType.getParameter(name); + String s2 = mediaType.getParameter(name); + if (StringUtils.hasText(s1) && StringUtils.hasText(s2) && !s1.equalsIgnoreCase(s2)) { + return false; + } + } + return true; + } + // Server side only... 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 86966b8a7a..6405ea7088 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 @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * 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. @@ -16,6 +16,8 @@ package org.springframework.http.codec; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.Collections; @@ -31,13 +33,13 @@ import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import reactor.test.StepVerifier; -import org.springframework.core.codec.Encoder; import org.springframework.core.io.buffer.DataBuffer; import org.springframework.core.io.buffer.DefaultDataBufferFactory; import org.springframework.http.MediaType; import org.springframework.mock.http.server.reactive.test.MockServerHttpResponse; import org.springframework.util.MimeType; import org.springframework.util.MimeTypeUtils; +import org.springframework.util.ReflectionUtils; import static java.nio.charset.StandardCharsets.*; import static org.junit.Assert.*; @@ -59,7 +61,7 @@ public class EncoderHttpMessageWriterTests { @Mock - private Encoder encoder; + private HttpMessageEncoder encoder; private ArgumentCaptor mediaTypeCaptor; @@ -172,6 +174,17 @@ public class EncoderHttpMessageWriterTests { assertEquals(0, this.response.getHeaders().getContentLength()); } + @Test // gh-22936 + public void isStreamingMediaType() throws InvocationTargetException, IllegalAccessException { + HttpMessageWriter writer = getWriter(TEXT_HTML); + MediaType streamingMediaType = new MediaType(TEXT_PLAIN, Collections.singletonMap("streaming", "true")); + when(this.encoder.getStreamingMediaTypes()).thenReturn(Arrays.asList(streamingMediaType)); + 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);