This is an automated email from the ASF dual-hosted git repository. rmaucher pushed a commit to branch 11.0.x in repository https://gitbox.apache.org/repos/asf/tomcat.git
commit de42994355e733f9e97bf2216533f6b087576835 Author: opencode <[email protected]> AuthorDate: Wed Oct 7 11:17:26 2026 +0200 Fix session histogram unlimited time bucket in the Manager The Manager text interface session histogram counted sessions with an unlimited timeout in a bucket selected by testing for a negative idle time. Idle time is never negative, so the branch was dead, the "Unlimited time" line was never displayed and sessions which never time out were silently miscounted into ordinary idle-time buckets. Count sessions with a negative max inactive interval explicitly as unlimited. A negative interval is the sentinel for "never times out"; note that a context session timeout of zero is stored as -1 minutes which becomes -60 seconds on sessions created for that context. The existing idle-based expiry behaviour triggered by the idle parameter is unchanged. Clamp a negative idle time - possible only if the system clock moves backwards - into the first bucket instead of letting it index the histogram array out of bounds, as the removed branch incidentally used to do. Add a test covering sessions with a max inactive interval of -1 and sessions created for a context with a session timeout of zero. --- .../apache/catalina/manager/ManagerServlet.java | 16 ++-- .../apache/catalina/manager/TestManagerWebapp.java | 87 ++++++++++++++++++++++ 2 files changed, 98 insertions(+), 5 deletions(-) diff --git a/java/org/apache/catalina/manager/ManagerServlet.java b/java/org/apache/catalina/manager/ManagerServlet.java index 0c55535fa4..14801d9dea 100644 --- a/java/org/apache/catalina/manager/ManagerServlet.java +++ b/java/org/apache/catalina/manager/ManagerServlet.java @@ -1292,13 +1292,19 @@ public class ManagerServlet extends HttpServlet implements ContainerServlet { session.expire(); expired++; } - time = time / 60 / histoInterval; - if (time < 0) { + // Negative interval: session never times out (a context + // session timeout of zero is stored as -1 minutes which + // becomes -60 seconds on the session) + if (session.getMaxInactiveInterval() < 0) { notimeout++; - } else if (time >= maxCount) { - timeout[maxCount]++; } else { - timeout[time]++; + // Clamp negative idle time (system clock went backwards) + time = Math.max(time, 0) / 60 / histoInterval; + if (time >= maxCount) { + timeout[maxCount]++; + } else { + timeout[time]++; + } } } if (timeout[0] > 0) { diff --git a/test/org/apache/catalina/manager/TestManagerWebapp.java b/test/org/apache/catalina/manager/TestManagerWebapp.java index 18bc35dd44..890d831f94 100644 --- a/test/org/apache/catalina/manager/TestManagerWebapp.java +++ b/test/org/apache/catalina/manager/TestManagerWebapp.java @@ -35,10 +35,12 @@ import org.apache.catalina.Context; import org.apache.catalina.Lifecycle; import org.apache.catalina.LifecycleEvent; import org.apache.catalina.LifecycleListener; +import org.apache.catalina.Session; import org.apache.catalina.authenticator.TestBasicAuthParser.BasicAuthHeader; import org.apache.catalina.filters.Constants; import org.apache.catalina.realm.MemoryRealm; import org.apache.catalina.realm.MessageDigestCredentialHandler; +import org.apache.catalina.session.ManagerBase; import org.apache.catalina.startup.Catalina; import org.apache.catalina.startup.HostConfig; import org.apache.catalina.startup.SimpleHttpClient; @@ -617,6 +619,91 @@ public class TestManagerWebapp extends TomcatBaseTest { } } + /* + * A session with an unlimited timeout (maxInactiveInterval == -1) must + * be counted in the "Unlimited time" bucket of the text interface + * session histogram rather than in an idle-time bucket. + */ + @Test + public void testSessionsUnlimitedTimeout() throws Exception { + ignoreTearDown = true; + Tomcat tomcat = getTomcatInstance(); + tomcat.setAddDefaultWebXmlToWebapp(false); + tomcat.addUser("admin", "sekr3t"); + tomcat.addRole("admin", "manager-script"); + + File webappDir = new File(getBuildDirectory(), "webapps"); + File appDir = new File(webappDir, "manager"); + tomcat.addWebapp(null, "/manager", appDir.getAbsolutePath()); + + Context ctx = tomcat.addContext("/testsession", null); + // A session timeout of zero means sessions never time out. It is + // stored as -1 minutes which becomes -60 seconds on the session. + Context zeroCtx = tomcat.addContext("/testsession0", null); + zeroCtx.setSessionTimeout(0); + + tomcat.start(); + + ManagerBase manager = (ManagerBase) ctx.getManager(); + Session unlimited = manager.createSession(null); + unlimited.setMaxInactiveInterval(-1); + manager.add(unlimited); + + Session limited = manager.createSession(null); + manager.add(limited); + + ManagerBase zeroManager = (ManagerBase) zeroCtx.getManager(); + Session zeroTimeout = zeroManager.createSession(null); + Assert.assertEquals(-60, zeroTimeout.getMaxInactiveInterval()); + zeroManager.add(zeroTimeout); + + SimpleHttpClient client = new SimpleHttpClient() { + @Override + public boolean isResponseBodyOK() { + return true; + } + }; + client.setPort(getPort()); + String basicHeader = + (new BasicAuthHeader("Basic", "admin", "sekr3t")).getHeader().toString(); + + // @formatter:off + client.setRequest(new String[] { + "GET /manager/text/sessions?path=/testsession HTTP/1.1" + CRLF + + "Host: localhost" + CRLF + + "Authorization: " + basicHeader + CRLF + + "Connection: Close" + CRLF + + CRLF + }); + // @formatter:on + client.connect(); + client.processRequest(true); + Assert.assertEquals(HttpServletResponse.SC_OK, client.getStatusCode()); + Assert.assertTrue(client.getResponseBody(), + client.getResponseBody().contains("Unlimited time: [1] sessions")); + // The unlimited session must not also appear in an idle bucket + Assert.assertTrue(client.getResponseBody(), + client.getResponseBody().contains("Inactive for [<1] minutes: [1] sessions")); + + // @formatter:off + client.setRequest(new String[] { + "GET /manager/text/sessions?path=/testsession0 HTTP/1.1" + CRLF + + "Host: localhost" + CRLF + + "Authorization: " + basicHeader + CRLF + + "Connection: Close" + CRLF + + CRLF + }); + // @formatter:on + client.connect(); + client.processRequest(true); + Assert.assertEquals(HttpServletResponse.SC_OK, client.getStatusCode()); + Assert.assertTrue(client.getResponseBody(), + client.getResponseBody().contains("Unlimited time: [1] sessions")); + + tomcat.stop(); + } + + /* * Verify that manager JSPs compile and render correctly. */ --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
