Skip to content

Commit 98efa1d

Browse files
committed
Merge branch '6.1.x'
2 parents 87ea3bd + 5680d20 commit 98efa1d

File tree

2 files changed

+114
-32
lines changed

2 files changed

+114
-32
lines changed

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

Lines changed: 28 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,12 @@
3838
/**
3939
* {@link jakarta.servlet.http.HttpServletResponse} wrapper that caches all content written to
4040
* the {@linkplain #getOutputStream() output stream} and {@linkplain #getWriter() writer},
41-
* and allows this content to be retrieved via a {@link #getContentAsByteArray() byte array}.
41+
* and allows this content to be retrieved via a {@linkplain #getContentAsByteArray() byte array}.
4242
*
4343
* <p>Used e.g. by {@link org.springframework.web.filter.ShallowEtagHeaderFilter}.
4444
*
4545
* @author Juergen Hoeller
46+
* @author Sam Brannen
4647
* @since 4.1.3
4748
* @see ContentCachingRequestWrapper
4849
*/
@@ -120,9 +121,16 @@ public PrintWriter getWriter() throws IOException {
120121
return this.writer;
121122
}
122123

124+
/**
125+
* This method neither flushes content to the client nor commits the underlying
126+
* response, since the content has not yet been copied to the response.
127+
* <p>Invoke {@link #copyBodyToResponse()} to copy the cached body content to
128+
* the wrapped response object and flush its buffer.
129+
* @see jakarta.servlet.ServletResponseWrapper#flushBuffer()
130+
*/
123131
@Override
124132
public void flushBuffer() throws IOException {
125-
// do not flush the underlying response as the content has not been copied to it yet
133+
// no-op
126134
}
127135

128136
@Override
@@ -139,31 +147,30 @@ public void setContentLengthLong(long len) {
139147
throw new IllegalArgumentException("Content-Length exceeds ContentCachingResponseWrapper's maximum (" +
140148
Integer.MAX_VALUE + "): " + len);
141149
}
142-
int lenInt = (int) len;
143-
if (lenInt > this.content.size()) {
144-
this.content.resize(lenInt);
145-
}
146-
this.contentLength = lenInt;
150+
setContentLength((int) len);
147151
}
148152

149153
@Override
150-
public void setContentType(String type) {
154+
public void setContentType(@Nullable String type) {
151155
this.contentType = type;
152156
}
153157

154158
@Override
155159
@Nullable
156160
public String getContentType() {
157-
return this.contentType;
161+
if (this.contentType != null) {
162+
return this.contentType;
163+
}
164+
return super.getContentType();
158165
}
159166

160167
@Override
161168
public boolean containsHeader(String name) {
162-
if (HttpHeaders.CONTENT_LENGTH.equalsIgnoreCase(name)) {
163-
return this.contentLength != null;
169+
if (this.contentLength != null && HttpHeaders.CONTENT_LENGTH.equalsIgnoreCase(name)) {
170+
return true;
164171
}
165-
else if (HttpHeaders.CONTENT_TYPE.equalsIgnoreCase(name)) {
166-
return this.contentType != null;
172+
else if (this.contentType != null && HttpHeaders.CONTENT_TYPE.equalsIgnoreCase(name)) {
173+
return true;
167174
}
168175
else {
169176
return super.containsHeader(name);
@@ -219,10 +226,10 @@ public void addIntHeader(String name, int value) {
219226
@Override
220227
@Nullable
221228
public String getHeader(String name) {
222-
if (HttpHeaders.CONTENT_LENGTH.equalsIgnoreCase(name)) {
223-
return (this.contentLength != null) ? this.contentLength.toString() : null;
229+
if (this.contentLength != null && HttpHeaders.CONTENT_LENGTH.equalsIgnoreCase(name)) {
230+
return this.contentLength.toString();
224231
}
225-
else if (HttpHeaders.CONTENT_TYPE.equalsIgnoreCase(name)) {
232+
else if (this.contentType != null && HttpHeaders.CONTENT_TYPE.equalsIgnoreCase(name)) {
226233
return this.contentType;
227234
}
228235
else {
@@ -232,12 +239,11 @@ else if (HttpHeaders.CONTENT_TYPE.equalsIgnoreCase(name)) {
232239

233240
@Override
234241
public Collection<String> getHeaders(String name) {
235-
if (HttpHeaders.CONTENT_LENGTH.equalsIgnoreCase(name)) {
236-
return this.contentLength != null ? Collections.singleton(this.contentLength.toString()) :
237-
Collections.emptySet();
242+
if (this.contentLength != null && HttpHeaders.CONTENT_LENGTH.equalsIgnoreCase(name)) {
243+
return Collections.singleton(this.contentLength.toString());
238244
}
239-
else if (HttpHeaders.CONTENT_TYPE.equalsIgnoreCase(name)) {
240-
return this.contentType != null ? Collections.singleton(this.contentType) : Collections.emptySet();
245+
else if (this.contentType != null && HttpHeaders.CONTENT_TYPE.equalsIgnoreCase(name)) {
246+
return Collections.singleton(this.contentType);
241247
}
242248
else {
243249
return super.getHeaders(name);
@@ -327,7 +333,7 @@ protected void copyBodyToResponse(boolean complete) throws IOException {
327333
}
328334
this.contentLength = null;
329335
}
330-
if (complete || this.contentType != null) {
336+
if (this.contentType != null) {
331337
rawResponse.setContentType(this.contentType);
332338
this.contentType = null;
333339
}

spring-web/src/test/java/org/springframework/web/filter/ContentCachingResponseWrapperTests.java

Lines changed: 86 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,54 +16,130 @@
1616

1717
package org.springframework.web.filter;
1818

19-
import java.nio.charset.StandardCharsets;
20-
2119
import jakarta.servlet.http.HttpServletResponse;
2220
import org.junit.jupiter.api.Test;
2321

24-
import org.springframework.http.HttpHeaders;
22+
import org.springframework.http.MediaType;
2523
import org.springframework.util.FileCopyUtils;
2624
import org.springframework.web.testfixture.servlet.MockHttpServletResponse;
2725
import org.springframework.web.util.ContentCachingResponseWrapper;
2826

27+
import static java.nio.charset.StandardCharsets.UTF_8;
2928
import static org.assertj.core.api.Assertions.assertThat;
29+
import static org.springframework.http.HttpHeaders.CONTENT_LENGTH;
30+
import static org.springframework.http.HttpHeaders.CONTENT_TYPE;
31+
import static org.springframework.http.HttpHeaders.TRANSFER_ENCODING;
3032

3133
/**
3234
* Tests for {@link ContentCachingResponseWrapper}.
3335
*
3436
* @author Rossen Stoyanchev
37+
* @author Sam Brannen
3538
*/
3639
class ContentCachingResponseWrapperTests {
3740

3841
@Test
3942
void copyBodyToResponse() throws Exception {
40-
byte[] responseBody = "Hello World".getBytes(StandardCharsets.UTF_8);
43+
byte[] responseBody = "Hello World".getBytes(UTF_8);
4144
MockHttpServletResponse response = new MockHttpServletResponse();
4245

4346
ContentCachingResponseWrapper responseWrapper = new ContentCachingResponseWrapper(response);
4447
responseWrapper.setStatus(HttpServletResponse.SC_OK);
4548
FileCopyUtils.copy(responseBody, responseWrapper.getOutputStream());
4649
responseWrapper.copyBodyToResponse();
4750

48-
assertThat(response.getStatus()).isEqualTo(200);
51+
assertThat(response.getStatus()).isEqualTo(HttpServletResponse.SC_OK);
4952
assertThat(response.getContentLength()).isGreaterThan(0);
5053
assertThat(response.getContentAsByteArray()).isEqualTo(responseBody);
5154
}
5255

56+
@Test
57+
void copyBodyToResponseWithPresetHeaders() throws Exception {
58+
String PUZZLE = "puzzle";
59+
String ENIGMA = "enigma";
60+
String NUMBER = "number";
61+
String MAGIC = "42";
62+
63+
byte[] responseBody = "Hello World".getBytes(UTF_8);
64+
String responseLength = Integer.toString(responseBody.length);
65+
String contentType = MediaType.APPLICATION_JSON_VALUE;
66+
67+
MockHttpServletResponse response = new MockHttpServletResponse();
68+
response.setContentType(contentType);
69+
response.setContentLength(999);
70+
response.setHeader(PUZZLE, ENIGMA);
71+
response.setIntHeader(NUMBER, 42);
72+
73+
ContentCachingResponseWrapper responseWrapper = new ContentCachingResponseWrapper(response);
74+
responseWrapper.setStatus(HttpServletResponse.SC_OK);
75+
76+
assertThat(responseWrapper.getStatus()).isEqualTo(HttpServletResponse.SC_OK);
77+
assertThat(responseWrapper.getContentSize()).isZero();
78+
assertThat(responseWrapper.getHeaderNames())
79+
.containsExactlyInAnyOrder(PUZZLE, NUMBER, CONTENT_TYPE, CONTENT_LENGTH);
80+
81+
assertThat(responseWrapper.containsHeader(PUZZLE)).as(PUZZLE).isTrue();
82+
assertThat(responseWrapper.getHeader(PUZZLE)).as(PUZZLE).isEqualTo(ENIGMA);
83+
assertThat(responseWrapper.getHeaders(PUZZLE)).as(PUZZLE).containsExactly(ENIGMA);
84+
85+
assertThat(responseWrapper.containsHeader(NUMBER)).as(NUMBER).isTrue();
86+
assertThat(responseWrapper.getHeader(NUMBER)).as(NUMBER).isEqualTo(MAGIC);
87+
assertThat(responseWrapper.getHeaders(NUMBER)).as(NUMBER).containsExactly(MAGIC);
88+
89+
assertThat(responseWrapper.containsHeader(CONTENT_TYPE)).as(CONTENT_TYPE).isTrue();
90+
assertThat(responseWrapper.getHeader(CONTENT_TYPE)).as(CONTENT_TYPE).isEqualTo(contentType);
91+
assertThat(responseWrapper.getHeaders(CONTENT_TYPE)).as(CONTENT_TYPE).containsExactly(contentType);
92+
assertThat(responseWrapper.getContentType()).as(CONTENT_TYPE).isEqualTo(contentType);
93+
94+
assertThat(responseWrapper.containsHeader(CONTENT_LENGTH)).as(CONTENT_LENGTH).isTrue();
95+
assertThat(responseWrapper.getHeader(CONTENT_LENGTH)).as(CONTENT_LENGTH).isEqualTo("999");
96+
assertThat(responseWrapper.getHeaders(CONTENT_LENGTH)).as(CONTENT_LENGTH).containsExactly("999");
97+
98+
FileCopyUtils.copy(responseBody, responseWrapper.getOutputStream());
99+
responseWrapper.copyBodyToResponse();
100+
101+
assertThat(responseWrapper.getHeaderNames())
102+
.containsExactlyInAnyOrder(PUZZLE, NUMBER, CONTENT_TYPE, CONTENT_LENGTH);
103+
104+
assertThat(response.getStatus()).isEqualTo(HttpServletResponse.SC_OK);
105+
assertThat(response.getContentType()).isEqualTo(contentType);
106+
assertThat(response.getContentLength()).isEqualTo(responseBody.length);
107+
assertThat(response.getContentAsByteArray()).isEqualTo(responseBody);
108+
assertThat(response.getHeaderNames())
109+
.containsExactlyInAnyOrder(PUZZLE, NUMBER, CONTENT_TYPE, CONTENT_LENGTH);
110+
111+
assertThat(response.containsHeader(PUZZLE)).as(PUZZLE).isTrue();
112+
assertThat(response.getHeader(PUZZLE)).as(PUZZLE).isEqualTo(ENIGMA);
113+
assertThat(response.getHeaders(PUZZLE)).as(PUZZLE).containsExactly(ENIGMA);
114+
115+
assertThat(response.containsHeader(NUMBER)).as(NUMBER).isTrue();
116+
assertThat(response.getHeader(NUMBER)).as(NUMBER).isEqualTo(MAGIC);
117+
assertThat(response.getHeaders(NUMBER)).as(NUMBER).containsExactly(MAGIC);
118+
119+
assertThat(response.containsHeader(CONTENT_TYPE)).as(CONTENT_TYPE).isTrue();
120+
assertThat(response.getHeader(CONTENT_TYPE)).as(CONTENT_TYPE).isEqualTo(contentType);
121+
assertThat(response.getHeaders(CONTENT_TYPE)).as(CONTENT_TYPE).containsExactly(contentType);
122+
assertThat(response.getContentType()).as(CONTENT_TYPE).isEqualTo(contentType);
123+
124+
assertThat(response.containsHeader(CONTENT_LENGTH)).as(CONTENT_LENGTH).isTrue();
125+
assertThat(response.getHeader(CONTENT_LENGTH)).as(CONTENT_LENGTH).isEqualTo(responseLength);
126+
assertThat(response.getHeaders(CONTENT_LENGTH)).as(CONTENT_LENGTH).containsExactly(responseLength);
127+
}
128+
53129
@Test
54130
void copyBodyToResponseWithTransferEncoding() throws Exception {
55-
byte[] responseBody = "6\r\nHello 5\r\nWorld0\r\n\r\n".getBytes(StandardCharsets.UTF_8);
131+
byte[] responseBody = "6\r\nHello 5\r\nWorld0\r\n\r\n".getBytes(UTF_8);
56132
MockHttpServletResponse response = new MockHttpServletResponse();
57133

58134
ContentCachingResponseWrapper responseWrapper = new ContentCachingResponseWrapper(response);
59135
responseWrapper.setStatus(HttpServletResponse.SC_OK);
60-
responseWrapper.setHeader(HttpHeaders.TRANSFER_ENCODING, "chunked");
136+
responseWrapper.setHeader(TRANSFER_ENCODING, "chunked");
61137
FileCopyUtils.copy(responseBody, responseWrapper.getOutputStream());
62138
responseWrapper.copyBodyToResponse();
63139

64-
assertThat(response.getStatus()).isEqualTo(200);
65-
assertThat(response.getHeader(HttpHeaders.TRANSFER_ENCODING)).isEqualTo("chunked");
66-
assertThat(response.getHeader(HttpHeaders.CONTENT_LENGTH)).isNull();
140+
assertThat(response.getStatus()).isEqualTo(HttpServletResponse.SC_OK);
141+
assertThat(response.getHeader(TRANSFER_ENCODING)).isEqualTo("chunked");
142+
assertThat(response.getHeader(CONTENT_LENGTH)).isNull();
67143
assertThat(response.getContentAsByteArray()).isEqualTo(responseBody);
68144
}
69145

0 commit comments

Comments
 (0)