diff --git a/spring-web/src/main/java/org/springframework/web/util/ContentCachingResponseWrapper.java b/spring-web/src/main/java/org/springframework/web/util/ContentCachingResponseWrapper.java index 5de12302320..00dd8027894 100644 --- a/spring-web/src/main/java/org/springframework/web/util/ContentCachingResponseWrapper.java +++ b/spring-web/src/main/java/org/springframework/web/util/ContentCachingResponseWrapper.java @@ -38,7 +38,7 @@ import org.springframework.util.FastByteArrayOutputStream; /** * {@link javax.servlet.http.HttpServletResponse} wrapper that caches all content written to * the {@linkplain #getOutputStream() output stream} and {@linkplain #getWriter() writer}, - * and allows this content to be retrieved via a {@link #getContentAsByteArray() byte array}. + * and allows this content to be retrieved via a {@linkplain #getContentAsByteArray() byte array}. * *

Used e.g. by {@link org.springframework.web.filter.ShallowEtagHeaderFilter}. * Note: As of Spring Framework 5.0, this wrapper is built on the Servlet 3.1 API. @@ -122,9 +122,16 @@ public class ContentCachingResponseWrapper extends HttpServletResponseWrapper { return this.writer; } + /** + * This method neither flushes content to the client nor commits the underlying + * response, since the content has not yet been copied to the response. + *

Invoke {@link #copyBodyToResponse()} to copy the cached body content to + * the wrapped response object and flush its buffer. + * @see javax.servlet.ServletResponseWrapper#flushBuffer() + */ @Override public void flushBuffer() throws IOException { - // do not flush the underlying response as the content has not been copied to it yet + // no-op } @Override @@ -142,15 +149,11 @@ public class ContentCachingResponseWrapper extends HttpServletResponseWrapper { throw new IllegalArgumentException("Content-Length exceeds ContentCachingResponseWrapper's maximum (" + Integer.MAX_VALUE + "): " + len); } - int lenInt = (int) len; - if (lenInt > this.content.size()) { - this.content.resize(lenInt); - } - this.contentLength = lenInt; + setContentLength((int) len); } @Override - public void setContentType(String type) { + public void setContentType(@Nullable String type) { this.contentType = type; } diff --git a/spring-web/src/test/java/org/springframework/web/filter/ContentCachingResponseWrapperTests.java b/spring-web/src/test/java/org/springframework/web/filter/ContentCachingResponseWrapperTests.java index 464198916a0..de05b070f69 100644 --- a/spring-web/src/test/java/org/springframework/web/filter/ContentCachingResponseWrapperTests.java +++ b/spring-web/src/test/java/org/springframework/web/filter/ContentCachingResponseWrapperTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2020 the original author or authors. + * Copyright 2002-2024 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,55 +16,69 @@ package org.springframework.web.filter; -import java.nio.charset.StandardCharsets; - import javax.servlet.http.HttpServletResponse; import org.junit.jupiter.api.Test; -import org.springframework.http.HttpHeaders; import org.springframework.util.FileCopyUtils; import org.springframework.web.testfixture.servlet.MockHttpServletResponse; import org.springframework.web.util.ContentCachingResponseWrapper; +import static java.nio.charset.StandardCharsets.UTF_8; import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.http.HttpHeaders.CONTENT_LENGTH; +import static org.springframework.http.HttpHeaders.TRANSFER_ENCODING; /** * Unit tests for {@link ContentCachingResponseWrapper}. + * * @author Rossen Stoyanchev */ public class ContentCachingResponseWrapperTests { @Test void copyBodyToResponse() throws Exception { - byte[] responseBody = "Hello World".getBytes(StandardCharsets.UTF_8); + byte[] responseBody = "Hello World".getBytes(UTF_8); MockHttpServletResponse response = new MockHttpServletResponse(); ContentCachingResponseWrapper responseWrapper = new ContentCachingResponseWrapper(response); - responseWrapper.setStatus(HttpServletResponse.SC_OK); + responseWrapper.setStatus(HttpServletResponse.SC_CREATED); FileCopyUtils.copy(responseBody, responseWrapper.getOutputStream()); responseWrapper.copyBodyToResponse(); - assertThat(response.getStatus()).isEqualTo(200); - assertThat(response.getContentLength() > 0).isTrue(); + assertThat(response.getStatus()).isEqualTo(HttpServletResponse.SC_CREATED); + assertThat(response.getContentLength()).isGreaterThan(0); assertThat(response.getContentAsByteArray()).isEqualTo(responseBody); } @Test void copyBodyToResponseWithTransferEncoding() throws Exception { - byte[] responseBody = "6\r\nHello 5\r\nWorld0\r\n\r\n".getBytes(StandardCharsets.UTF_8); + byte[] responseBody = "6\r\nHello 5\r\nWorld0\r\n\r\n".getBytes(UTF_8); MockHttpServletResponse response = new MockHttpServletResponse(); ContentCachingResponseWrapper responseWrapper = new ContentCachingResponseWrapper(response); - responseWrapper.setStatus(HttpServletResponse.SC_OK); - responseWrapper.setHeader(HttpHeaders.TRANSFER_ENCODING, "chunked"); + responseWrapper.setStatus(HttpServletResponse.SC_CREATED); + responseWrapper.setHeader(TRANSFER_ENCODING, "chunked"); FileCopyUtils.copy(responseBody, responseWrapper.getOutputStream()); responseWrapper.copyBodyToResponse(); - assertThat(response.getStatus()).isEqualTo(200); - assertThat(response.getHeader(HttpHeaders.TRANSFER_ENCODING)).isEqualTo("chunked"); - assertThat(response.getHeader(HttpHeaders.CONTENT_LENGTH)).isNull(); + assertThat(response.getStatus()).isEqualTo(HttpServletResponse.SC_CREATED); + assertHeader(response, TRANSFER_ENCODING, "chunked"); + assertHeader(response, CONTENT_LENGTH, null); assertThat(response.getContentAsByteArray()).isEqualTo(responseBody); } + private void assertHeader(HttpServletResponse response, String header, String value) { + if (value == null) { + assertThat(response.containsHeader(header)).as(header).isFalse(); + assertThat(response.getHeader(header)).as(header).isNull(); + assertThat(response.getHeaders(header)).as(header).isEmpty(); + } + else { + assertThat(response.containsHeader(header)).as(header).isTrue(); + assertThat(response.getHeader(header)).as(header).isEqualTo(value); + assertThat(response.getHeaders(header)).as(header).containsExactly(value); + } + } + }