diff --git a/core/src/main/java/org/apache/struts2/interceptor/ActionFileUploadInterceptor.java b/core/src/main/java/org/apache/struts2/interceptor/ActionFileUploadInterceptor.java
index a0197e6c39..3b6ef08aa0 100644
--- a/core/src/main/java/org/apache/struts2/interceptor/ActionFileUploadInterceptor.java
+++ b/core/src/main/java/org/apache/struts2/interceptor/ActionFileUploadInterceptor.java
@@ -22,6 +22,7 @@
import com.opensymphony.xwork2.ActionProxy;
import com.opensymphony.xwork2.interceptor.ValidationAware;
import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletRequestWrapper;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.struts2.action.UploadedFilesAware;
@@ -135,7 +136,11 @@ public class ActionFileUploadInterceptor extends AbstractFileUploadInterceptor {
@Override
public String intercept(ActionInvocation invocation) throws Exception {
HttpServletRequest request = invocation.getInvocationContext().getServletRequest();
- if (!(request instanceof MultiPartRequestWrapper multiWrapper)) {
+ MultiPartRequestWrapper multiWrapper = request instanceof HttpServletRequestWrapper wrapper
+ ? findMultipartRequestWrapper(wrapper)
+ : null;
+
+ if (multiWrapper == null) {
if (LOG.isDebugEnabled()) {
ActionProxy proxy = invocation.getProxy();
LOG.debug(getTextMessage(STRUTS_MESSAGES_BYPASS_REQUEST_KEY, new String[]{proxy.getNamespace(), proxy.getActionName()}));
@@ -145,8 +150,8 @@ public String intercept(ActionInvocation invocation) throws Exception {
if (!(invocation.getAction() instanceof UploadedFilesAware action)) {
LOG.debug("Action: {} doesn't implement: {}, ignoring file upload",
- invocation.getProxy().getActionName(),
- UploadedFilesAware.class.getSimpleName());
+ invocation.getProxy().getActionName(),
+ UploadedFilesAware.class.getSimpleName());
return invocation.invoke();
}
@@ -185,5 +190,26 @@ public String intercept(ActionInvocation invocation) throws Exception {
return invocation.invoke();
}
+ /**
+ * Tries to find {@link MultiPartRequestWrapper} as the request can be already wrapped
+ * with another {@link HttpServletRequestWrapper}.
+ * If the {@link MultiPartRequestWrapper} cannot be found, null is returned instead.
+ *
+ * @param request current {@link HttpServletRequestWrapper}
+ * @return {@link MultiPartRequestWrapper} or null
+ * @since 7.0.0
+ */
+ protected MultiPartRequestWrapper findMultipartRequestWrapper(HttpServletRequestWrapper request) {
+ if (request instanceof MultiPartRequestWrapper multiPartRequestWrapper) {
+ LOG.debug("Found multipart request: {}", multiPartRequestWrapper.getClass().getSimpleName());
+ return multiPartRequestWrapper;
+ } else if (request.getRequest() instanceof HttpServletRequestWrapper wrappedRequest) {
+ LOG.debug("Could not find multipart request wrapper, checking ancestor: {}",
+ wrappedRequest.getClass().getSimpleName());
+ return findMultipartRequestWrapper(wrappedRequest);
+ }
+ return null;
+ }
+
}
diff --git a/core/src/test/java/org/apache/struts2/interceptor/ActionFileUploadInterceptorTest.java b/core/src/test/java/org/apache/struts2/interceptor/ActionFileUploadInterceptorTest.java
index ec1195490f..178d6bfc02 100644
--- a/core/src/test/java/org/apache/struts2/interceptor/ActionFileUploadInterceptorTest.java
+++ b/core/src/test/java/org/apache/struts2/interceptor/ActionFileUploadInterceptorTest.java
@@ -25,6 +25,7 @@
import com.opensymphony.xwork2.mock.MockActionInvocation;
import com.opensymphony.xwork2.mock.MockActionProxy;
import com.opensymphony.xwork2.util.ClassLoaderUtil;
+import jakarta.servlet.http.HttpServletRequestWrapper;
import org.apache.commons.fileupload2.jakarta.servlet6.JakartaServletDiskFileUpload;
import org.apache.commons.fileupload2.jakarta.servlet6.JakartaServletFileUpload;
import org.apache.struts2.StrutsInternalTestCase;
@@ -514,6 +515,67 @@ public void testMultipartRequestLocalizedError() throws Exception {
assertTrue(msg.startsWith("Der Request übertraf die maximal erlaubte Größe"));
}
+ public void testSingleHttpRequestWrapper() throws Exception {
+ request.setCharacterEncoding(StandardCharsets.UTF_8.name());
+ request.setMethod("post");
+ request.addHeader("Content-type", "multipart/form-data; boundary=---1234");
+
+ // inspired by the unit tests for jakarta commons fileupload
+ String content = ("""
+ -----1234\r
+ Content-Disposition: form-data; name="file"; filename="deleteme.txt"\r
+ Content-Type: text/html\r
+ \r
+ Unit test of ActionFileUploadInterceptor\r
+ -----1234--\r
+ """);
+ request.setContent(content.getBytes(StandardCharsets.UTF_8));
+ MyFileUploadAction action = container.inject(MyFileUploadAction.class);
+
+ MockActionInvocation mai = new MockActionInvocation();
+ mai.setAction(action);
+ mai.setResultCode("success");
+ mai.setInvocationContext(ActionContext.getContext());
+ ActionContext.getContext()
+ .withServletRequest(new HttpServletRequestWrapper(createMultipartRequestMaxSize(1000)));
+
+ String result = interceptor.intercept(mai);
+
+ assertFalse(action.hasActionErrors());
+ assertEquals("success", result);
+ }
+
+ public void testMultipleHttpRequestWrappers() throws Exception {
+ request.setCharacterEncoding(StandardCharsets.UTF_8.name());
+ request.setMethod("post");
+ request.addHeader("Content-type", "multipart/form-data; boundary=---1234");
+
+ // inspired by the unit tests for jakarta commons fileupload
+ String content = ("""
+ -----1234\r
+ Content-Disposition: form-data; name="file"; filename="deleteme.txt"\r
+ Content-Type: text/html\r
+ \r
+ Unit test of ActionFileUploadInterceptor\r
+ -----1234--\r
+ """);
+ request.setContent(content.getBytes(StandardCharsets.US_ASCII));
+
+ MyFileUploadAction action = container.inject(MyFileUploadAction.class);
+
+ MockActionInvocation mai = new MockActionInvocation();
+ mai.setAction(action);
+ mai.setResultCode("success");
+ mai.setInvocationContext(ActionContext.getContext());
+ ActionContext.getContext()
+ .withServletRequest(new HttpServletRequestWrapper(new HttpServletRequestWrapper(createMultipartRequestMaxSize(1000))));
+
+ String result = interceptor.intercept(mai);
+
+ assertFalse(action.hasActionErrors());
+ assertEquals("success", result);
+ }
+
private String encodeTextFile(String filename, String contentType, String content) {
return endline +
"--" + boundary +
diff --git a/core/src/test/resources/log4j2.xml b/core/src/test/resources/log4j2.xml
index 88dd6e0f6f..4c4061dbfa 100644
--- a/core/src/test/resources/log4j2.xml
+++ b/core/src/test/resources/log4j2.xml
@@ -29,6 +29,5 @@
-
\ No newline at end of file