diff --git a/servlet/src/main/java/com/twelvemonkeys/servlet/image/ImageFilter.java b/servlet/src/main/java/com/twelvemonkeys/servlet/image/ImageFilter.java index 9a9f79ac..a4615ad7 100755 --- a/servlet/src/main/java/com/twelvemonkeys/servlet/image/ImageFilter.java +++ b/servlet/src/main/java/com/twelvemonkeys/servlet/image/ImageFilter.java @@ -114,9 +114,7 @@ public abstract class ImageFilter extends GenericFilter { // Make image available to other filters (avoid unnecessary serializing/deserializing) imageResponse.setImage(image); //System.out.println("Done."); - } - if (encode) { //System.out.println("Encoding image..."); // Encode image to original response diff --git a/servlet/src/main/java/com/twelvemonkeys/servlet/image/ImageServletResponseImpl.java b/servlet/src/main/java/com/twelvemonkeys/servlet/image/ImageServletResponseImpl.java index 60f75370..9f79f4a7 100755 --- a/servlet/src/main/java/com/twelvemonkeys/servlet/image/ImageServletResponseImpl.java +++ b/servlet/src/main/java/com/twelvemonkeys/servlet/image/ImageServletResponseImpl.java @@ -189,7 +189,7 @@ class ImageServletResponseImpl extends HttpServletResponseWrapper implements Ima String outputType = getOutputContentType(); // Force transcoding, if no other filtering is done - if (!outputType.equals(originalContentType)) { + if (outputType != null && !outputType.equals(originalContentType)) { getImage(); } diff --git a/servlet/src/test/java/com/twelvemonkeys/servlet/image/ImageFilterTestCase.java b/servlet/src/test/java/com/twelvemonkeys/servlet/image/ImageFilterTestCase.java index c9d18d35..45a7da87 100644 --- a/servlet/src/test/java/com/twelvemonkeys/servlet/image/ImageFilterTestCase.java +++ b/servlet/src/test/java/com/twelvemonkeys/servlet/image/ImageFilterTestCase.java @@ -88,6 +88,8 @@ public class ImageFilterTestCase { // Verifications verify(chain).doFilter(request, response); + verify(response, never()).getOutputStream(); + verify(response, never()).getWriter(); } @Test @@ -342,6 +344,160 @@ public class ImageFilterTestCase { // decoded once, then encoded once } + // TODO: Add various test cases where resource/upstream filter fails (response.sendError + exception) + + @Test(expected = ServletException.class) + public void passThroughIfNotTriggerException() throws ServletException, IOException { + // Filter init & setup + ServletContext context = mock(ServletContext.class); + + FilterConfig filterConfig = mock(FilterConfig.class); + when(filterConfig.getFilterName()).thenReturn("dummy"); + when(filterConfig.getServletContext()).thenReturn(context); + when(filterConfig.getInitParameterNames()).thenReturn(new StringTokenIterator("foo, bar")); + + DummyFilter filter = new DummyFilter() { + @Override + protected boolean trigger(ServletRequest pRequest) { + return false; + } + }; + filter.init(filterConfig); + + // Request/response setup + HttpServletRequest request = mock(HttpServletRequest.class); + HttpServletResponse response = mock(HttpServletResponse.class); + + FilterChain chain = mock(FilterChain.class); + doThrow(new ServletException("Something went terribly wrong. Take shelter.")).when(chain).doFilter(any(ServletRequest.class), any(ServletResponse.class)); + + // Execute + try { + filter.doFilter(request, response, chain); + } + finally { + // Verifications + verify(chain).doFilter(request, response); + verify(response, never()).getOutputStream(); + verify(response, never()).getWriter(); + } + } + + @Test(expected = ServletException.class) + public void normalFilterException() throws ServletException, IOException { + // Filter init & setup + ServletContext context = mock(ServletContext.class); + + FilterConfig filterConfig = mock(FilterConfig.class); + when(filterConfig.getFilterName()).thenReturn("dummy"); + when(filterConfig.getServletContext()).thenReturn(context); + when(filterConfig.getInitParameterNames()).thenReturn(new StringTokenIterator("foo, bar")); + + DummyFilter filter = new DummyFilter(); + filter.init(filterConfig); + + // Request/response setup + ByteArrayOutputStream stream = new ByteArrayOutputStream(); + ServletOutputStream out = spy(new OutputStreamAdapter(stream)); + + HttpServletRequest request = mock(HttpServletRequest.class); + HttpServletResponse response = mock(HttpServletResponse.class); + when(response.getOutputStream()).thenReturn(out); + + FilterChain chain = mock(FilterChain.class); + doThrow(new ServletException("Something went terribly wrong. Take shelter.")).when(chain).doFilter(any(ServletRequest.class), any(ServletResponse.class)); + + // Execute + try { + filter.doFilter(request, response, chain); + } + finally { + // Verifications + verify(chain).doFilter(any(ServletRequest.class), any(ServletResponse.class)); + } + } + + @Test + public void passThroughIfNotTriggerSendError() throws ServletException, IOException { + // Filter init & setup + ServletContext context = mock(ServletContext.class); + + FilterConfig filterConfig = mock(FilterConfig.class); + when(filterConfig.getFilterName()).thenReturn("dummy"); + when(filterConfig.getServletContext()).thenReturn(context); + when(filterConfig.getInitParameterNames()).thenReturn(new StringTokenIterator("foo, bar")); + + DummyFilter filter = new DummyFilter() { + @Override + protected boolean trigger(ServletRequest pRequest) { + return false; + } + }; + filter.init(filterConfig); + + // Request/response setup + HttpServletRequest request = mock(HttpServletRequest.class); + HttpServletResponse response = mock(HttpServletResponse.class); + + FilterChain chain = mock(FilterChain.class); + doAnswer(new Answer() { + public Void answer(InvocationOnMock invocation) throws Throwable { + HttpServletResponse response = (HttpServletResponse) invocation.getArguments()[1]; + response.sendError(HttpServletResponse.SC_FORBIDDEN, "I'm afraid I can't do that."); + + return null; + } + }).when(chain).doFilter(any(ServletRequest.class), any(ServletResponse.class)); + + // Execute + filter.doFilter(request, response, chain); + + // Verifications + verify(chain).doFilter(request, response); + verify(response, never()).getOutputStream(); + verify(response, never()).getWriter(); + } + + @Test + public void normalFilterSendError() throws ServletException, IOException { + // Filter init & setup + ServletContext context = mock(ServletContext.class); + + FilterConfig filterConfig = mock(FilterConfig.class); + when(filterConfig.getFilterName()).thenReturn("dummy"); + when(filterConfig.getServletContext()).thenReturn(context); + when(filterConfig.getInitParameterNames()).thenReturn(new StringTokenIterator("foo, bar")); + + DummyFilter filter = new DummyFilter(); + filter.init(filterConfig); + + // Request/response setup + ByteArrayOutputStream stream = new ByteArrayOutputStream(); + ServletOutputStream out = spy(new OutputStreamAdapter(stream)); + + HttpServletRequest request = mock(HttpServletRequest.class); + HttpServletResponse response = mock(HttpServletResponse.class); + when(response.getOutputStream()).thenReturn(out); + + FilterChain chain = mock(FilterChain.class); + doAnswer(new Answer() { + public Void answer(InvocationOnMock invocation) throws Throwable { + HttpServletResponse response = (HttpServletResponse) invocation.getArguments()[1]; + response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "I've just picked up a fault in the AE35 unit."); + + return null; + } + }).when(chain).doFilter(any(ServletRequest.class), any(ServletResponse.class)); + + // Execute + filter.doFilter(request, response, chain); + + // Verifications + verify(chain).doFilter(any(ServletRequest.class), any(ServletResponse.class)); + verify(response, atMost(1)).setContentLength(stream.size()); // setContentLength not implemented, avoid future bugs + verify(out, atLeastOnce()).flush(); + } + private static class DummyFilter extends ImageFilter { @Override protected RenderedImage doFilter(BufferedImage image, ServletRequest request, ImageServletResponse response) throws IOException {