From f1b42b7d8f268923bfb57869b2ced3f11e1a4a75 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Tue, 30 Jun 2015 14:25:57 +0200 Subject: [PATCH] ContentCachingResponseWrapper defensively applies content length in case of sendError/sendRedirect Issue: SPR-13004 (cherry picked from commit 4facb2f) --- .../util/ContentCachingResponseWrapper.java | 31 ++++++++++++++++--- .../filter/ShallowEtagHeaderFilterTests.java | 13 +++++--- 2 files changed, 34 insertions(+), 10 deletions(-) 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 8c4e8412401..ff358bea917 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 @@ -48,6 +48,8 @@ public class ContentCachingResponseWrapper extends HttpServletResponseWrapper { private int statusCode = HttpServletResponse.SC_OK; + private Integer contentLength; + /** * Create a new ContentCachingResponseWrapper for the given servlet response. @@ -74,14 +76,27 @@ public class ContentCachingResponseWrapper extends HttpServletResponseWrapper { @Override public void sendError(int sc) throws IOException { copyBodyToResponse(); - super.sendError(sc); + try { + super.sendError(sc); + } + catch (IllegalStateException ex) { + // Possibly on Tomcat when called too late: fall back to silent setStatus + super.setStatus(sc); + } this.statusCode = sc; } @Override + @SuppressWarnings("deprecation") public void sendError(int sc, String msg) throws IOException { copyBodyToResponse(); - super.sendError(sc, msg); + try { + super.sendError(sc, msg); + } + catch (IllegalStateException ex) { + // Possibly on Tomcat when called too late: fall back to silent setStatus + super.setStatus(sc, msg); + } this.statusCode = sc; } @@ -111,6 +126,7 @@ public class ContentCachingResponseWrapper extends HttpServletResponseWrapper { if (len > this.content.capacity()) { this.content.resize(len); } + this.contentLength = len; } // Overrides Servlet 3.1 setContentLengthLong(long) at runtime @@ -119,9 +135,11 @@ public class ContentCachingResponseWrapper extends HttpServletResponseWrapper { throw new IllegalArgumentException("Content-Length exceeds ShallowEtagHeaderFilter's maximum (" + Integer.MAX_VALUE + "): " + len); } - if (len > this.content.capacity()) { - this.content.resize((int) len); + int lenInt = (int) len; + if (lenInt > this.content.capacity()) { + this.content.resize(lenInt); } + this.contentLength = lenInt; } @Override @@ -158,7 +176,10 @@ public class ContentCachingResponseWrapper extends HttpServletResponseWrapper { private void copyBodyToResponse() throws IOException { if (this.content.size() > 0) { - getResponse().setContentLength(this.content.size()); + if (this.contentLength != null) { + getResponse().setContentLength(this.contentLength); + this.contentLength = null; + } StreamUtils.copy(this.content.toByteArray(), getResponse().getOutputStream()); this.content.reset(); } diff --git a/spring-web/src/test/java/org/springframework/web/filter/ShallowEtagHeaderFilterTests.java b/spring-web/src/test/java/org/springframework/web/filter/ShallowEtagHeaderFilterTests.java index 74d501147b3..b573a06d463 100644 --- a/spring-web/src/test/java/org/springframework/web/filter/ShallowEtagHeaderFilterTests.java +++ b/spring-web/src/test/java/org/springframework/web/filter/ShallowEtagHeaderFilterTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2015 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. @@ -58,7 +58,7 @@ public class ShallowEtagHeaderFilterTests { assertFalse(filter.isEligibleForEtag(request, response, 200, new byte[0])); request = new MockHttpServletRequest("POST", "/hotels"); - request.addHeader("Cache-Control","must-revalidate, no-store"); + request.addHeader("Cache-Control", "must-revalidate, no-store"); assertFalse(filter.isEligibleForEtag(request, response, 200, new byte[0])); } @@ -146,6 +146,7 @@ public class ShallowEtagHeaderFilterTests { public void doFilter(ServletRequest filterRequest, ServletResponse filterResponse) throws IOException, ServletException { assertEquals("Invalid request passed", request, filterRequest); + response.setContentLength(100); FileCopyUtils.copy(responseBody, filterResponse.getOutputStream()); ((HttpServletResponse) filterResponse).sendError(HttpServletResponse.SC_FORBIDDEN); } @@ -154,7 +155,7 @@ public class ShallowEtagHeaderFilterTests { assertEquals("Invalid status", 403, response.getStatus()); assertNull("Invalid ETag header", response.getHeader("ETag")); - assertTrue("Invalid Content-Length header", response.getContentLength() > 0); + assertEquals("Invalid Content-Length header", 100, response.getContentLength()); assertArrayEquals("Invalid content", responseBody, response.getContentAsByteArray()); } @@ -169,6 +170,7 @@ public class ShallowEtagHeaderFilterTests { public void doFilter(ServletRequest filterRequest, ServletResponse filterResponse) throws IOException, ServletException { assertEquals("Invalid request passed", request, filterRequest); + response.setContentLength(100); FileCopyUtils.copy(responseBody, filterResponse.getOutputStream()); ((HttpServletResponse) filterResponse).sendError(HttpServletResponse.SC_FORBIDDEN, "ERROR"); } @@ -177,7 +179,7 @@ public class ShallowEtagHeaderFilterTests { assertEquals("Invalid status", 403, response.getStatus()); assertNull("Invalid ETag header", response.getHeader("ETag")); - assertTrue("Invalid Content-Length header", response.getContentLength() > 0); + assertEquals("Invalid Content-Length header", 100, response.getContentLength()); assertArrayEquals("Invalid content", responseBody, response.getContentAsByteArray()); assertEquals("Invalid error message", "ERROR", response.getErrorMessage()); } @@ -193,6 +195,7 @@ public class ShallowEtagHeaderFilterTests { public void doFilter(ServletRequest filterRequest, ServletResponse filterResponse) throws IOException, ServletException { assertEquals("Invalid request passed", request, filterRequest); + response.setContentLength(100); FileCopyUtils.copy(responseBody, filterResponse.getOutputStream()); ((HttpServletResponse) filterResponse).sendRedirect("http://www.google.com"); } @@ -201,7 +204,7 @@ public class ShallowEtagHeaderFilterTests { assertEquals("Invalid status", 302, response.getStatus()); assertNull("Invalid ETag header", response.getHeader("ETag")); - assertTrue("Invalid Content-Length header", response.getContentLength() > 0); + assertEquals("Invalid Content-Length header", 100, response.getContentLength()); assertArrayEquals("Invalid content", responseBody, response.getContentAsByteArray()); assertEquals("Invalid redirect URL", "http://www.google.com", response.getRedirectedUrl()); }