This is an automated email from the ASF dual-hosted git repository. rmaucher pushed a commit to branch 9.0.x in repository https://gitbox.apache.org/repos/asf/tomcat.git
commit ffa7c574a1c8f21197efd0dc949e736ea9a2b5d0 Author: opencode <[email protected]> AuthorDate: Thu Oct 8 14:19:01 2026 +0200 Fix SSI built-in date variables always resolving to null The mediator stored the built-in variables DATE_GMT, DATE_LOCAL and LAST_MODIFIED as external variables using names starting with its own fully qualified class name, and looked them up the same way. The servlet resolver rejects names under the reserved prefixes, and since org.apache.catalina.ssi.SSIMediator falls under the org.apache.catalina. prefix added when the reserved list was extended, both the store and the lookup silently did nothing and the variables always rendered as (none). Hold the built-in variables in the mediator itself, as per-document values that do not need to live in the external resolver, and resolve them there after the external lookup, preserving the ability of the document to override them via #set. The cross-mediator alreadyset guard was dead (it used the same rejected names) and has no meaning with per-mediator storage; with it gone, the fromConstructor parameter of setConfigTimeFmt() and setDateVariables() is no longer needed and is removed. --- java/org/apache/catalina/ssi/SSIMediator.java | 67 ++++++++++-------------- test/org/apache/catalina/ssi/TestSsiServlet.java | 3 ++ webapps/docs/changelog.xml | 12 +++++ 3 files changed, 43 insertions(+), 39 deletions(-) diff --git a/java/org/apache/catalina/ssi/SSIMediator.java b/java/org/apache/catalina/ssi/SSIMediator.java index 0f2140d693..f949445eb6 100644 --- a/java/org/apache/catalina/ssi/SSIMediator.java +++ b/java/org/apache/catalina/ssi/SSIMediator.java @@ -20,8 +20,10 @@ import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.Collection; import java.util.Date; +import java.util.HashMap; import java.util.HashSet; import java.util.Locale; +import java.util.Map; import java.util.Set; import java.util.TimeZone; import java.util.regex.Matcher; @@ -100,6 +102,12 @@ public class SSIMediator { * Number of regex match groups from the last match operation. */ protected int lastMatchCount = 0; + /** + * Built-in variables supplied by this mediator, keyed by upper-case name. These are held + * locally rather than in the external resolver since the resolver rejects names under the + * reserved prefixes, which included the names historically used for these variables. + */ + private final Map<String, String> builtinVariables = new HashMap<>(); /** @@ -111,7 +119,7 @@ public class SSIMediator { public SSIMediator(SSIExternalResolver ssiExternalResolver, long lastModifiedDate) { this.ssiExternalResolver = ssiExternalResolver; this.lastModifiedDate = lastModifiedDate; - setConfigTimeFmt(DEFAULT_CONFIG_TIME_FMT, true); + setConfigTimeFmt(DEFAULT_CONFIG_TIME_FMT); } @@ -131,24 +139,13 @@ public class SSIMediator { * @param configTimeFmt the time format string */ public void setConfigTimeFmt(String configTimeFmt) { - setConfigTimeFmt(configTimeFmt, false); - } - - - /** - * Sets the time format string and updates date variables accordingly. - * - * @param configTimeFmt the time format string - * @param fromConstructor true if called from the constructor - */ - public void setConfigTimeFmt(String configTimeFmt, boolean fromConstructor) { this.configTimeFmt = configTimeFmt; this.strftime = new Strftime(configTimeFmt, Locale.US); /* * Variables like DATE_LOCAL, DATE_GMT, and LAST_MODIFIED need to be updated when the timefmt changes. This is * what Apache SSI does. */ - setDateVariables(fromConstructor); + setDateVariables(); } @@ -308,8 +305,7 @@ public class SSIMediator { // Try getting it externally first, if it fails, try getting the 'built-in' value variableValue = ssiExternalResolver.getVariableValue(variableName); if (variableValue == null) { - variableName = variableName.toUpperCase(Locale.ENGLISH); - variableValue = ssiExternalResolver.getVariableValue(className + "." + variableName); + variableValue = builtinVariables.get(variableName.toUpperCase(Locale.ENGLISH)); } if (variableValue != null) { variableValue = encode(variableValue, encoding); @@ -517,30 +513,23 @@ public class SSIMediator { /** * Updates the built-in date variables (DATE_GMT, DATE_LOCAL, LAST_MODIFIED). - * - * @param fromConstructor true if called from the constructor - */ - protected void setDateVariables(boolean fromConstructor) { - boolean alreadySet = ssiExternalResolver.getVariableValue(className + ".alreadyset") != null; - // skip this if we are being called from the constructor, and this has already been set - if (!(fromConstructor && alreadySet)) { - ssiExternalResolver.setVariableValue(className + ".alreadyset", "true"); - Date date = new Date(); - TimeZone timeZone = TimeZone.getTimeZone("GMT"); - String retVal = formatDate(date, timeZone); - /* - * If we are setting on of the date variables, we want to remove them from the user defined list of - * variables, because this is what Apache does. - */ - setVariableValue("DATE_GMT", null); - ssiExternalResolver.setVariableValue(className + ".DATE_GMT", retVal); - retVal = formatDate(date, null); - setVariableValue("DATE_LOCAL", null); - ssiExternalResolver.setVariableValue(className + ".DATE_LOCAL", retVal); - retVal = formatDate(new Date(lastModifiedDate), null); - setVariableValue("LAST_MODIFIED", null); - ssiExternalResolver.setVariableValue(className + ".LAST_MODIFIED", retVal); - } + */ + protected void setDateVariables() { + Date date = new Date(); + TimeZone timeZone = TimeZone.getTimeZone("GMT"); + String retVal = formatDate(date, timeZone); + /* + * If we are setting on of the date variables, we want to remove them from the user defined list of variables, + * because this is what Apache does. + */ + setVariableValue("DATE_GMT", null); + builtinVariables.put("DATE_GMT", retVal); + retVal = formatDate(date, null); + setVariableValue("DATE_LOCAL", null); + builtinVariables.put("DATE_LOCAL", retVal); + retVal = formatDate(new Date(lastModifiedDate), null); + setVariableValue("LAST_MODIFIED", null); + builtinVariables.put("LAST_MODIFIED", retVal); } diff --git a/test/org/apache/catalina/ssi/TestSsiServlet.java b/test/org/apache/catalina/ssi/TestSsiServlet.java index 251cc6957b..e20d9660c3 100644 --- a/test/org/apache/catalina/ssi/TestSsiServlet.java +++ b/test/org/apache/catalina/ssi/TestSsiServlet.java @@ -67,6 +67,9 @@ public class TestSsiServlet extends TomcatBaseTest { Assert.assertTrue(body.contains("path is relative")); Assert.assertTrue(body.contains("1k")); Assert.assertTrue(body.contains("SERVER_PROTOCOL")); + // Any undefined variable renders as "(none)". The page only echoes + // variables that must be defined, including the built-in date variables. + Assert.assertFalse(body.contains("(none)")); } } diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml index 792dd44af7..2c9e539ee3 100644 --- a/webapps/docs/changelog.xml +++ b/webapps/docs/changelog.xml @@ -1932,6 +1932,18 @@ evaluation of existing documents using unparenthesized mixed <code>&&</code> and <code>||</code> chains. (remm) </fix> + <fix> + Fix the built-in SSI variables <code>DATE_GMT</code>, + <code>DATE_LOCAL</code> and <code>LAST_MODIFIED</code> always + resolving to <code>null</code> and rendering as + <code>(none)</code>. The mediator stored and looked them up as + external variables using names starting with its own fully qualified + class name, which the servlet resolver rejects since those names fall + under the reserved <code>org.apache.catalina.</code> prefix. Hold the + built-in variables in the mediator itself and resolve them there, as + they are per-document values that do not need to be stored in the + external resolver. (remm) + </fix> </changelog> </subsection> <subsection name="Coyote"> --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
