This is an automated email from the ASF dual-hosted git repository. lukaszlenart pushed a commit to branch fix/WW-5548-forward in repository https://gitbox.apache.org/repos/asf/struts.git
commit ab727f3d16b38953dd35219f3e7d4e0abd594e18 Author: Lukasz Lenart <lukaszlen...@apache.org> AuthorDate: Thu May 8 07:58:19 2025 +0200 WW-5548 Defines proper request attributes when forwarding or including final path --- .../struts2/result/ServletDispatcherResult.java | 30 ++++++++++++++------ .../result/ServletDispatcherResultTest.java | 33 ++++++++++++++++++---- 2 files changed, 49 insertions(+), 14 deletions(-) diff --git a/core/src/main/java/org/apache/struts2/result/ServletDispatcherResult.java b/core/src/main/java/org/apache/struts2/result/ServletDispatcherResult.java index feced67bb..3d8aeb8f3 100644 --- a/core/src/main/java/org/apache/struts2/result/ServletDispatcherResult.java +++ b/core/src/main/java/org/apache/struts2/result/ServletDispatcherResult.java @@ -18,8 +18,6 @@ */ package org.apache.struts2.result; -import org.apache.struts2.ActionInvocation; -import org.apache.struts2.inject.Inject; import jakarta.servlet.RequestDispatcher; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; @@ -28,9 +26,11 @@ import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.StringUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.apache.struts2.ActionInvocation; import org.apache.struts2.ServletActionContext; import org.apache.struts2.StrutsStatics; import org.apache.struts2.dispatcher.HttpParameters; +import org.apache.struts2.inject.Inject; import org.apache.struts2.url.QueryStringParser; import java.io.Serial; @@ -158,13 +158,6 @@ public class ServletDispatcherResult extends StrutsResultSupport { //if we are inside an action tag, we always need to do an include boolean insideActionTag = (Boolean) ObjectUtils.defaultIfNull(request.getAttribute(StrutsStatics.STRUTS_ACTION_TAG_INVOCATION), Boolean.FALSE); - // this should allow integration with third-party view related frameworks - if (finalLocation.contains("?")) { - request.setAttribute(RequestDispatcher.FORWARD_SERVLET_PATH, finalLocation.substring(0, finalLocation.indexOf('?'))); - } else { - request.setAttribute(RequestDispatcher.FORWARD_SERVLET_PATH, finalLocation); - } - // If we're included, then include the view // Otherwise do forward // This allow the page to, for example, set content type @@ -173,9 +166,28 @@ public class ServletDispatcherResult extends StrutsResultSupport { request.setAttribute("struts.view_uri", finalLocation); request.setAttribute("struts.request_uri", request.getRequestURI()); + // These attributes are required when forwarding to another servlet, see specification: + // https://jakarta.ee/specifications/servlet/6.0/jakarta-servlet-spec-6.0#forwarded-request-parameters + request.setAttribute(RequestDispatcher.FORWARD_MAPPING, request.getHttpServletMapping()); + request.setAttribute(RequestDispatcher.FORWARD_REQUEST_URI, request.getRequestURI()); + request.setAttribute(RequestDispatcher.FORWARD_CONTEXT_PATH, request.getContextPath()); + request.setAttribute(RequestDispatcher.FORWARD_SERVLET_PATH, request.getServletPath()); + request.setAttribute(RequestDispatcher.FORWARD_PATH_INFO, request.getPathInfo()); + request.setAttribute(RequestDispatcher.FORWARD_QUERY_STRING, request.getQueryString()); + dispatcher.forward(request, response); } else { LOG.debug("Including location: {}", finalLocation); + + // These attributes are required when forwarding to another servlet, see specification: + // https://jakarta.ee/specifications/servlet/6.0/jakarta-servlet-spec-6.0#included-request-parameters + request.setAttribute(RequestDispatcher.INCLUDE_MAPPING, request.getHttpServletMapping()); + request.setAttribute(RequestDispatcher.INCLUDE_REQUEST_URI, request.getRequestURI()); + request.setAttribute(RequestDispatcher.INCLUDE_CONTEXT_PATH, request.getContextPath()); + request.setAttribute(RequestDispatcher.INCLUDE_SERVLET_PATH, request.getServletPath()); + request.setAttribute(RequestDispatcher.INCLUDE_PATH_INFO, request.getPathInfo()); + request.setAttribute(RequestDispatcher.INCLUDE_QUERY_STRING, request.getQueryString()); + dispatcher.include(request, response); } } diff --git a/core/src/test/java/org/apache/struts2/result/ServletDispatcherResultTest.java b/core/src/test/java/org/apache/struts2/result/ServletDispatcherResultTest.java index c64c9d76a..c14332665 100644 --- a/core/src/test/java/org/apache/struts2/result/ServletDispatcherResultTest.java +++ b/core/src/test/java/org/apache/struts2/result/ServletDispatcherResultTest.java @@ -39,30 +39,54 @@ public class ServletDispatcherResultTest extends StrutsInternalTestCase implemen ServletDispatcherResult view = new ServletDispatcherResult(); view.setLocation("foo.jsp"); + request.setRequestURI("/app/namespace/my.action"); + request.setContextPath("/app"); + request.setServletPath("/namespace/my.action"); + request.setPathInfo(null); + request.setQueryString("a=1&b=2"); + request.setAttribute("struts.actiontag.invocation", null); request.setAttribute("jakarta.servlet.include.servlet_path", null); - request.setRequestURI("foo.jsp"); response.setCommitted(Boolean.FALSE); view.execute(invocation); assertEquals("foo.jsp", response.getForwardedUrl()); - assertEquals("foo.jsp", request.getAttribute(RequestDispatcher.FORWARD_SERVLET_PATH)); + + // Attributes required by Specification when forwarding to another resource + // https://jakarta.ee/specifications/servlet/6.0/jakarta-servlet-spec-6.0#forwarded-request-parameters + assertEquals("/app/namespace/my.action", request.getAttribute(RequestDispatcher.FORWARD_REQUEST_URI)); + assertEquals("/app", request.getAttribute(RequestDispatcher.FORWARD_CONTEXT_PATH)); + assertEquals("/namespace/my.action", request.getAttribute(RequestDispatcher.FORWARD_SERVLET_PATH)); + assertNull(request.getAttribute(RequestDispatcher.FORWARD_PATH_INFO)); + assertEquals("a=1&b=2", request.getAttribute(RequestDispatcher.FORWARD_QUERY_STRING)); } public void testInclude() throws Exception { ServletDispatcherResult view = new ServletDispatcherResult(); view.setLocation("foo.jsp"); + request.setRequestURI("/app/namespace/my.action"); + request.setContextPath("/app"); + request.setServletPath("/namespace/my.action"); + request.setPathInfo(null); + request.setQueryString("a=1&b=2"); + request.setAttribute("struts.actiontag.invocation", null); response.setCommitted(Boolean.TRUE); - request.setRequestURI("foo.jsp"); view.execute(invocation); assertEquals("foo.jsp", response.getIncludedUrl()); - assertEquals("foo.jsp", request.getAttribute(RequestDispatcher.FORWARD_SERVLET_PATH)); + + // These attributes must be set when including another resource + // https://jakarta.ee/specifications/servlet/6.0/jakarta-servlet-spec-6.0#included-request-parameters + assertEquals("/app/namespace/my.action", request.getAttribute(RequestDispatcher.INCLUDE_REQUEST_URI)); + assertEquals("/app", request.getAttribute(RequestDispatcher.INCLUDE_CONTEXT_PATH)); + assertEquals("/namespace/my.action", request.getAttribute(RequestDispatcher.INCLUDE_SERVLET_PATH)); + assertNull(request.getAttribute(RequestDispatcher.INCLUDE_PATH_INFO)); + assertEquals("a=1&b=2", request.getAttribute(RequestDispatcher.INCLUDE_QUERY_STRING)); } public void testWithParameter() throws Exception { @@ -76,7 +100,6 @@ public class ServletDispatcherResultTest extends StrutsInternalTestCase implemen // See https://issues.apache.org/jira/browse/WW-5486 assertEquals("1", stack.findString("#parameters.bar")); - assertEquals("foo.jsp", request.getAttribute(RequestDispatcher.FORWARD_SERVLET_PATH)); } @Override