Skip to content

Commit afd93a0

Browse files
committed
Disable streaming when reading to Resources in RestTemplate
Prior to this commit, the `ResourceHttpMessageConverter` would support converting from an `HttpInputMessage` to a `InputStreamResource`. This is valid when reading resources on the server side, but it's not compatible with the way `RestTemplate` works. The API exposed by `RestOperations` imply that the HTTP server response should be fully consumed and properly closed by the time the `exchange` method returns. In other words, this HTTP client does not support streaming the HTTP response. This commit allows the `ResourceHttpMessageConverter` to be configured to disable read streaming when used in `RestTemplate`. Issue: SPR-14882
1 parent 848a2b2 commit afd93a0

File tree

3 files changed

+39
-5
lines changed

3 files changed

+39
-5
lines changed

spring-web/src/main/java/org/springframework/http/converter/ResourceHttpMessageConverter.java

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2016 the original author or authors.
2+
* Copyright 2002-2017 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -49,9 +49,27 @@ public class ResourceHttpMessageConverter extends AbstractHttpMessageConverter<R
4949
private static final boolean jafPresent = ClassUtils.isPresent(
5050
"javax.activation.FileTypeMap", ResourceHttpMessageConverter.class.getClassLoader());
5151

52+
private final boolean supportsReadStreaming;
5253

54+
/**
55+
* Create a new instance of the {@code ResourceHttpMessageConverter}
56+
* that supports read streaming, i.e. can convert an
57+
* {@code HttpInputMessage} to {@code InputStreamResource}.
58+
*/
5359
public ResourceHttpMessageConverter() {
5460
super(MediaType.ALL);
61+
this.supportsReadStreaming = true;
62+
}
63+
64+
/**
65+
* Create a new instance of the {@code ResourceHttpMessageConverter}
66+
* @param supportsReadStreaming whether the converter should support
67+
* read streaming, i.e. convert to {@code InputStreamResource}.
68+
* @since 5.0
69+
*/
70+
public ResourceHttpMessageConverter(boolean supportsReadStreaming) {
71+
super(MediaType.ALL);
72+
this.supportsReadStreaming = supportsReadStreaming;
5573
}
5674

5775

@@ -64,7 +82,7 @@ protected boolean supports(Class<?> clazz) {
6482
protected Resource readInternal(Class<? extends Resource> clazz, HttpInputMessage inputMessage)
6583
throws IOException, HttpMessageNotReadableException {
6684

67-
if (InputStreamResource.class == clazz) {
85+
if (supportsReadStreaming && InputStreamResource.class == clazz) {
6886
return new InputStreamResource(inputMessage.getBody());
6987
}
7088
else if (clazz.isAssignableFrom(ByteArrayResource.class)) {

spring-web/src/main/java/org/springframework/web/client/RestTemplate.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2016 the original author or authors.
2+
* Copyright 2002-2017 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -161,7 +161,7 @@ public class RestTemplate extends InterceptingHttpAccessor implements RestOperat
161161
public RestTemplate() {
162162
this.messageConverters.add(new ByteArrayHttpMessageConverter());
163163
this.messageConverters.add(new StringHttpMessageConverter());
164-
this.messageConverters.add(new ResourceHttpMessageConverter());
164+
this.messageConverters.add(new ResourceHttpMessageConverter(false));
165165
this.messageConverters.add(new SourceHttpMessageConverter<>());
166166
this.messageConverters.add(new AllEncompassingFormHttpMessageConverter());
167167

spring-web/src/test/java/org/springframework/http/converter/ResourceHttpMessageConverterTests.java

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2016 the original author or authors.
2+
* Copyright 2002-2017 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -21,7 +21,9 @@
2121
import java.io.InputStream;
2222
import java.util.Arrays;
2323

24+
import org.junit.Rule;
2425
import org.junit.Test;
26+
import org.junit.rules.ExpectedException;
2527

2628
import org.springframework.core.io.ByteArrayResource;
2729
import org.springframework.core.io.ClassPathResource;
@@ -47,6 +49,9 @@ public class ResourceHttpMessageConverterTests {
4749

4850
private final ResourceHttpMessageConverter converter = new ResourceHttpMessageConverter();
4951

52+
@Rule
53+
public ExpectedException thrown = ExpectedException.none();
54+
5055

5156
@Test
5257
public void canReadResource() {
@@ -79,6 +84,17 @@ public void shouldReadInputStreamResource() throws IOException {
7984
}
8085
}
8186

87+
@Test // SPR-14882
88+
public void shouldNotReadInputStreamResource() throws IOException {
89+
ResourceHttpMessageConverter noStreamConverter = new ResourceHttpMessageConverter(false);
90+
try (InputStream body = getClass().getResourceAsStream("logo.jpg") ) {
91+
this.thrown.expect(IllegalStateException.class);
92+
MockHttpInputMessage inputMessage = new MockHttpInputMessage(body);
93+
inputMessage.getHeaders().setContentType(MediaType.IMAGE_JPEG);
94+
noStreamConverter.read(InputStreamResource.class, inputMessage);
95+
}
96+
}
97+
8298
@Test
8399
public void shouldWriteImageResource() throws IOException {
84100
MockHttpOutputMessage outputMessage = new MockHttpOutputMessage();

0 commit comments

Comments
 (0)