WW-5602 fix StreamResult contentCharSet handling (#1511)

Evaluates contentCharSet expression before emptiness check to prevent
malformed content-type headers when expression evaluates to null.

- Parse contentCharSet expression first, then check if result is empty
- Use StringUtils.isNotEmpty() for proper null/empty validation
- Use setCharacterEncoding() instead of appending to content-type string
- Add test for null-evaluating charset expressions

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
Lukasz Lenart
2026-01-14 19:18:27 +01:00
committed by GitHub
parent 6d20e33a37
commit c60670b22b
3 changed files with 24 additions and 7 deletions
+1
View File
@@ -148,6 +148,7 @@ Each plugin is a separate Maven module with:
### Important Notes
- **Version**: Currently 6.7.5-SNAPSHOT (release branch: `release/struts-6-7-x`)
- **Java Compatibility**: Compiled for Java 8, tested through Java 21
- **Servlet API**: Uses javax.servlet (Java EE), NOT Jakarta EE (jakarta.servlet)
- **Security**: Always validate inputs and follow OWASP guidelines
- **Performance**: Leverage built-in caching (OGNL expressions, templates)
- **Deprecation**: Some legacy XWork components marked for removal
@@ -21,6 +21,7 @@ package org.apache.struts2.result;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.inject.Inject;
import com.opensymphony.xwork2.security.NotExcludedAcceptedPatternsChecker;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@@ -223,7 +224,7 @@ public class StreamResult extends StrutsResultSupport {
if (inputStream == null) {
String msg = ("Can not find a java.io.InputStream with the name [" + parsedInputName + "] in the invocation stack. " +
"Check the <param name=\"inputName\"> tag specified for this action is correct, not excluded and accepted.");
"Check the <param name=\"inputName\"> tag specified for this action is correct, not excluded and accepted.");
LOG.error(msg);
throw new IllegalArgumentException(msg);
}
@@ -231,11 +232,12 @@ public class StreamResult extends StrutsResultSupport {
HttpServletResponse oResponse = invocation.getInvocationContext().getServletResponse();
LOG.debug("Set the content type: {};charset{}", contentType, contentCharSet);
if (contentCharSet != null && !contentCharSet.equals("")) {
oResponse.setContentType(conditionalParse(contentType, invocation) + ";charset=" + conditionalParse(contentCharSet, invocation));
} else {
oResponse.setContentType(conditionalParse(contentType, invocation));
LOG.debug("Set the content type: {};charset={}", contentType, contentCharSet);
String parsedContentType = conditionalParse(contentType, invocation);
String parsedContentCharSet = conditionalParse(contentCharSet, invocation);
oResponse.setContentType(parsedContentType);
if (StringUtils.isNotEmpty(parsedContentCharSet)) {
oResponse.setCharacterEncoding(parsedContentCharSet);
}
LOG.debug("Set the content length: {}", contentLength);
@@ -267,7 +269,7 @@ public class StreamResult extends StrutsResultSupport {
oOutput = oResponse.getOutputStream();
LOG.debug("Streaming result [{}] type=[{}] length=[{}] content-disposition=[{}] charset=[{}]",
inputName, contentType, contentLength, contentDisposition, contentCharSet);
inputName, contentType, contentLength, contentDisposition, contentCharSet);
LOG.debug("Streaming to output buffer +++ START +++");
byte[] oBuff = new byte[bufferSize];
@@ -120,6 +120,16 @@ public class StreamResultTest extends StrutsInternalTestCase {
assertEquals("inline", response.getHeader("Content-disposition"));
}
public void testStreamResultWithNullCharSetExpression() throws Exception {
result.setParse(true);
result.setInputName("streamForImage");
result.setContentCharSet("${nullCharSetMethod}");
result.doExecute("helloworld", mai);
assertEquals("text/plain", response.getContentType());
}
public void testAllowCacheDefault() throws Exception {
result.setInputName("streamForImage");
@@ -310,6 +320,10 @@ public class StreamResultTest extends StrutsInternalTestCase {
public String getContentCharSetMethod() {
return "UTF-8";
}
public String getNullCharSetMethod() {
return null;
}
}
}