This is an automated email from the ASF dual-hosted git repository.

radu pushed a commit to branch master
in repository 
https://gitbox.apache.org/repos/asf/sling-org-apache-sling-engine.git


The following commit(s) were added to refs/heads/master by this push:
     new 5092904  SLING-12003 - The RequestDispatcher should flush the buffer 
on forward only if the buffer hasn't already been closed
5092904 is described below

commit 50929040170d7f9f8ba31c8baec6c95ec6c5fa81
Author: Radu Cotescu <[email protected]>
AuthorDate: Tue Aug 15 15:17:30 2023 +0200

    SLING-12003 - The RequestDispatcher should flush the buffer on forward only 
if the buffer hasn't already been closed
    
    * flush only if the response has not been committed
---
 .../impl/request/SlingRequestDispatcher.java       |   7 +-
 .../impl/request/SlingRequestDispatcherTest.java   | 138 +++++++++++++++++++++
 2 files changed, 142 insertions(+), 3 deletions(-)

diff --git 
a/src/main/java/org/apache/sling/engine/impl/request/SlingRequestDispatcher.java
 
b/src/main/java/org/apache/sling/engine/impl/request/SlingRequestDispatcher.java
index 60782c8..b064d51 100644
--- 
a/src/main/java/org/apache/sling/engine/impl/request/SlingRequestDispatcher.java
+++ 
b/src/main/java/org/apache/sling/engine/impl/request/SlingRequestDispatcher.java
@@ -123,9 +123,10 @@ public class SlingRequestDispatcher implements 
RequestDispatcher {
         dispatch(request, response, info);
 
         // finally, we would have to ensure the response is committed
-        // and closed. Let's just flush the buffer and thus commit the
-        // response for now
-        response.flushBuffer();
+        // and closed
+        if (!response.isCommitted()) {
+            response.flushBuffer();
+        }
     }
 
     private String getAbsolutePath(SlingHttpServletRequest request, String 
path) {
diff --git 
a/src/test/java/org/apache/sling/engine/impl/request/SlingRequestDispatcherTest.java
 
b/src/test/java/org/apache/sling/engine/impl/request/SlingRequestDispatcherTest.java
new file mode 100644
index 0000000..dc87601
--- /dev/null
+++ 
b/src/test/java/org/apache/sling/engine/impl/request/SlingRequestDispatcherTest.java
@@ -0,0 +1,138 @@
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ ~ Licensed to the Apache Software Foundation (ASF) under one
+ ~ or more contributor license agreements.  See the NOTICE file
+ ~ distributed with this work for additional information
+ ~ regarding copyright ownership.  The ASF licenses this file
+ ~ to you under the Apache License, Version 2.0 (the
+ ~ "License"); you may not use this file except in compliance
+ ~ with the License.  You may obtain a copy of the License at
+ ~
+ ~   http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing,
+ ~ software distributed under the License is distributed on an
+ ~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ ~ KIND, either express or implied.  See the License for the
+ ~ specific language governing permissions and limitations
+ ~ under the License.
+ 
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+package org.apache.sling.engine.impl.request;
+
+
+import java.io.IOException;
+import java.lang.reflect.Field;
+import java.util.concurrent.atomic.AtomicReference;
+
+import javax.servlet.RequestDispatcher;
+import javax.servlet.Servlet;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.sling.api.SlingHttpServletRequest;
+import org.apache.sling.api.SlingHttpServletResponse;
+import org.apache.sling.api.request.RequestDispatcherOptions;
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.api.resource.ResourceMetadata;
+import org.apache.sling.api.servlets.ServletResolver;
+import org.apache.sling.engine.impl.SlingHttpServletRequestImpl;
+import org.apache.sling.engine.impl.SlingHttpServletResponseImpl;
+import org.apache.sling.engine.impl.SlingRequestProcessorImpl;
+import org.apache.sling.engine.impl.filter.FilterHandle;
+import org.apache.sling.engine.impl.filter.ServletFilterManager;
+import org.jetbrains.annotations.NotNull;
+import org.junit.Test;
+
+import static org.junit.Assert.assertNotNull;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+public class SlingRequestDispatcherTest {
+
+    @Test
+    public void testForwardResponseBufferClosed() throws Exception {
+        Servlet forwardedServlet = mock(Servlet.class);
+        doAnswer(
+                invocationOnMock -> {
+                    ServletResponse servletResponse = 
invocationOnMock.getArgument(1);
+                    when(servletResponse.isCommitted()).thenReturn(true);
+                    doThrow(new IOException("Response is 
committed")).when(servletResponse).getWriter();
+                    doThrow(new IOException("Response is 
committed")).when(servletResponse).getOutputStream();
+                    doThrow(new IOException("Response is 
committed")).when(servletResponse).flushBuffer();
+                    verify(servletResponse, never()).flushBuffer();
+                    return null;
+                }
+        ).when(forwardedServlet).service(any(ServletRequest.class), 
any(ServletResponse.class));
+        testForwarding(forwardedServlet);
+    }
+
+    @Test
+    public void testForwardResponseBufferNotClosed() throws Exception {
+        Servlet forwardedServlet = mock(Servlet.class);
+        AtomicReference<ServletResponse> outerResponse = new 
AtomicReference<>();
+        doAnswer(
+                invocationOnMock -> {
+                    ServletResponse servletResponse = 
invocationOnMock.getArgument(1);
+                    when(servletResponse.isCommitted()).thenReturn(false);
+                    outerResponse.set(servletResponse);
+                    return null;
+                }
+        ).when(forwardedServlet).service(any(ServletRequest.class), 
any(ServletResponse.class));
+        testForwarding(forwardedServlet);
+        assertNotNull(outerResponse.get());
+        verify(outerResponse.get(), times(1)).flushBuffer();
+    }
+
+    private @NotNull Resource getMockedResource(@NotNull String path) {
+        Resource resource = mock(Resource.class);
+        when(resource.getPath()).thenReturn(path);
+        ResourceMetadata resourceMetadata = mock(ResourceMetadata.class);
+        when(resource.getResourceMetadata()).thenReturn(resourceMetadata);
+        when(resourceMetadata.getResolutionPathInfo()).thenReturn(path);
+        return resource;
+    }
+
+    private void testForwarding(@NotNull Servlet servlet) throws Exception {
+        Resource forwardResource = getMockedResource("/forward");
+
+        RequestDispatcher requestDispatcher = new 
SlingRequestDispatcher(forwardResource, new RequestDispatcherOptions(),
+                false, false);
+        SlingRequestProcessorImpl slingRequestProcessor = new 
SlingRequestProcessorImpl();
+
+        HttpServletRequest httpServletRequest = mock(HttpServletRequest.class);
+        HttpServletResponse httpServletResponse = 
mock(HttpServletResponse.class);
+
+        RequestData requestData = new RequestData(slingRequestProcessor, 
httpServletRequest, httpServletResponse,
+                false, false, false);
+        SlingHttpServletRequest request = spy(new 
SlingHttpServletRequestImpl(requestData, httpServletRequest));
+        SlingHttpServletResponse response = spy(new 
SlingHttpServletResponseImpl(requestData, httpServletResponse));
+        Resource initialResource = getMockedResource("/initial");
+        when(request.getResource()).thenReturn(initialResource);
+
+        ServletResolver servletResolver = mock(ServletResolver.class);
+        
when(servletResolver.resolveServlet(any(SlingHttpServletRequest.class))).thenReturn(servlet);
+        Field servletResolverField = 
slingRequestProcessor.getClass().getDeclaredField("servletResolver");
+        servletResolverField.setAccessible(true);
+        servletResolverField.set(slingRequestProcessor, servletResolver);
+
+        ServletFilterManager filterManager = mock(ServletFilterManager.class);
+        when(filterManager.getFilters(any())).thenReturn(new FilterHandle[]{});
+        Field filterManagerField = 
slingRequestProcessor.getClass().getDeclaredField("filterManager");
+        filterManagerField.setAccessible(true);
+        filterManagerField.set(slingRequestProcessor, filterManager);
+
+        requestData.initServlet(initialResource, servletResolver);
+
+        requestDispatcher.forward(request, response);
+        verify(servlet).service(any(ServletRequest.class), 
any(ServletResponse.class));
+    }
+
+}

Reply via email to