Skip to content

Commit c6250f5

Browse files
committed
Fix InputStream caching in ContentCachingReqWrapper
Prior to this commit, the ContentCachingRequestWrapper would immediately consume the wrapped request's InputStream when asked for the cached content; that caused several issues: * the request body was read in memory even if it wasn't yet consumed by the application, leading to inefficiencies. * when requesting the InputStream, an empty InputStream was returned since the original was already read. This case only happened for form POSTs requests. This commit makes sure that the wrapper does not alter the request expected behavior: * when getting the inputstream, it is wrapped in order to cache its content * when getting request parameters, the request body is cached and its inputstream is consumed, as expected Issue: SPR-12810
1 parent 88a1448 commit c6250f5

File tree

2 files changed

+52
-8
lines changed

2 files changed

+52
-8
lines changed

spring-web/src/main/java/org/springframework/web/util/ContentCachingRequestWrapper.java

Lines changed: 35 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
import java.io.ByteArrayOutputStream;
2121
import java.io.IOException;
2222
import java.io.InputStreamReader;
23-
import java.io.UnsupportedEncodingException;
2423
import java.net.URLEncoder;
2524
import java.util.Arrays;
2625
import java.util.Enumeration;
@@ -88,14 +87,36 @@ public BufferedReader getReader() throws IOException {
8887
return this.reader;
8988
}
9089

91-
/**
92-
* Return the cached request content as a byte array.
93-
*/
94-
public byte[] getContentAsByteArray() {
90+
@Override
91+
public String getParameter(String name) {
9592
if(this.cachedContent.size() == 0 && isFormPost()) {
9693
writeRequestParamsToContent();
9794
}
98-
return this.cachedContent.toByteArray();
95+
return super.getParameter(name);
96+
}
97+
98+
@Override
99+
public Map<String, String[]> getParameterMap() {
100+
if(this.cachedContent.size() == 0 && isFormPost()) {
101+
writeRequestParamsToContent();
102+
}
103+
return super.getParameterMap();
104+
}
105+
106+
@Override
107+
public Enumeration<String> getParameterNames() {
108+
if(this.cachedContent.size() == 0 && isFormPost()) {
109+
writeRequestParamsToContent();
110+
}
111+
return super.getParameterNames();
112+
}
113+
114+
@Override
115+
public String[] getParameterValues(String name) {
116+
if(this.cachedContent.size() == 0 && isFormPost()) {
117+
writeRequestParamsToContent();
118+
}
119+
return super.getParameterValues(name);
99120
}
100121

101122
private boolean isFormPost() {
@@ -107,7 +128,7 @@ private void writeRequestParamsToContent() {
107128
try {
108129
if (this.cachedContent.size() == 0) {
109130
String requestEncoding = getCharacterEncoding();
110-
Map<String, String[]> form = getParameterMap();
131+
Map<String, String[]> form = super.getParameterMap();
111132
for (Iterator<String> nameIterator = form.keySet().iterator(); nameIterator.hasNext(); ) {
112133
String name = nameIterator.next();
113134
List<String> values = Arrays.asList(form.get(name));
@@ -133,6 +154,13 @@ private void writeRequestParamsToContent() {
133154
}
134155
}
135156

157+
/**
158+
* Return the cached request content as a byte array.
159+
*/
160+
public byte[] getContentAsByteArray() {
161+
return this.cachedContent.toByteArray();
162+
}
163+
136164
private class ContentCachingInputStream extends ServletInputStream {
137165

138166
private final ServletInputStream is;
@@ -150,5 +178,4 @@ public int read() throws IOException {
150178
return ch;
151179
}
152180
}
153-
154181
}

spring-web/src/test/java/org/springframework/web/util/ContentCachingRequestWrapperTests.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,23 @@ public void requestParams() throws Exception {
6161
// getting request parameters will consume the request body
6262
Assert.assertFalse(wrapper.getParameterMap().isEmpty());
6363
Assert.assertEquals("first=value&second=foo&second=bar", new String(wrapper.getContentAsByteArray()));
64+
// SPR-12810 : inputstream body should be consumed
65+
Assert.assertEquals("", new String(FileCopyUtils.copyToByteArray(wrapper.getInputStream())));
66+
}
67+
68+
// SPR-12810
69+
@Test
70+
public void inputStreamFormPostRequest() throws Exception {
71+
this.request.setMethod("POST");
72+
this.request.setContentType(FORM_CONTENT_TYPE);
73+
this.request.setCharacterEncoding(CHARSET);
74+
this.request.setParameter("first", "value");
75+
this.request.setParameter("second", new String[] {"foo", "bar"});
76+
77+
ContentCachingRequestWrapper wrapper = new ContentCachingRequestWrapper(this.request);
78+
79+
byte[] response = FileCopyUtils.copyToByteArray(wrapper.getInputStream());
80+
Assert.assertArrayEquals(response, wrapper.getContentAsByteArray());
6481
}
6582

6683
}

0 commit comments

Comments
 (0)