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]

Reply via email to