From 34b6ecec6c53e3f94b4247dbac2f25d145bee727 Mon Sep 17 00:00:00 2001 From: Jan Bartel Date: Tue, 18 Dec 2018 09:32:12 +1100 Subject: [PATCH] Issue #3202 Ensure sessions created during async are cleaned up. Signed-off-by: Jan Bartel --- .../jetty/server/session/SessionHandler.java | 18 ++- .../jetty/server/session/TestFooServlet.java | 20 ++- .../jetty/server/session/AsyncTest.java | 119 ++++++++++++++++++ 3 files changed, 150 insertions(+), 7 deletions(-) create mode 100644 tests/test-sessions/test-sessions-common/src/test/java/org/eclipse/jetty/server/session/AsyncTest.java diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/SessionHandler.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/SessionHandler.java index abcd95afb3a..af5232ee7c6 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/session/SessionHandler.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/SessionHandler.java @@ -407,6 +407,9 @@ public class SessionHandler extends ScopedHandler */ public void complete(HttpSession session) { + if (LOG.isDebugEnabled()) + LOG.debug("Complete called with session {}", session); + if (session == null) return; @@ -428,6 +431,8 @@ public class SessionHandler extends ScopedHandler { if (request.isAsyncStarted() && request.getDispatcherType() == DispatcherType.REQUEST) { + if (LOG.isDebugEnabled()) + LOG.debug("Adding AsyncListener for {}", request); request.getAsyncContext().addListener(_sessionAsyncListener); } else @@ -1599,6 +1604,9 @@ public class SessionHandler extends ScopedHandler try { + if (LOG.isDebugEnabled()) + LOG.debug("SessionHandler.doScope"); + old_session_manager = baseRequest.getSessionHandler(); old_session = baseRequest.getSession(false); @@ -1622,10 +1630,7 @@ public class SessionHandler extends ScopedHandler } if (LOG.isDebugEnabled()) - { - LOG.debug("sessionHandler=" + this); - LOG.debug("session=" + existingSession); - } + LOG.debug("sessionHandler={} session={}",this, existingSession); if (_nextScope != null) _nextScope.doScope(target,baseRequest,request,response); @@ -1638,8 +1643,9 @@ public class SessionHandler extends ScopedHandler { //if there is a session that was created during handling this context, then complete it HttpSession finalSession = baseRequest.getSession(false); - if (LOG.isDebugEnabled()) LOG.debug("FinalSession="+finalSession+" old_session_manager="+old_session_manager+" this="+this); - if ((finalSession != null) && (old_session_manager != this)) + if (LOG.isDebugEnabled()) + LOG.debug("FinalSession={}, old_session_manager={}, this={}, calling complete={}", finalSession, old_session_manager, this, (old_session_manager != this)); + if (old_session_manager != this) { complete((Session)finalSession, baseRequest); } diff --git a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/TestFooServlet.java b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/TestFooServlet.java index 548a5498106..621c95b74c4 100644 --- a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/TestFooServlet.java +++ b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/TestFooServlet.java @@ -21,6 +21,7 @@ package org.eclipse.jetty.server.session; import java.io.IOException; import java.lang.reflect.Proxy; +import javax.servlet.AsyncContext; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; @@ -54,6 +55,23 @@ public class TestFooServlet extends HttpServlet if (foo == null || foo.getInt() != 33) response.sendError(500, "Foo not deserialized"); } - + else if ("async".equals(action)) + { + if (request.getAttribute("async-test") == null) + { + request.setAttribute("async-test", Boolean.TRUE); + AsyncContext acontext = request.startAsync(); + System.err.println("Starting async and dispatching"); + + acontext.dispatch(); + return; + } + else + { + HttpSession session = request.getSession(true); + System.err.println("After dispatch and finishing response"); + response.getWriter().println("OK"); + } + } } } diff --git a/tests/test-sessions/test-sessions-common/src/test/java/org/eclipse/jetty/server/session/AsyncTest.java b/tests/test-sessions/test-sessions-common/src/test/java/org/eclipse/jetty/server/session/AsyncTest.java new file mode 100644 index 00000000000..cfc683d2530 --- /dev/null +++ b/tests/test-sessions/test-sessions-common/src/test/java/org/eclipse/jetty/server/session/AsyncTest.java @@ -0,0 +1,119 @@ +// +// ======================================================================== +// Copyright (c) 1995-2018 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + + +package org.eclipse.jetty.server.session; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.io.IOException; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.eclipse.jetty.client.HttpClient; +import org.eclipse.jetty.client.api.ContentResponse; +import org.eclipse.jetty.servlet.ServletContextHandler; +import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.util.log.Log; +import org.eclipse.jetty.util.log.StacklessLogging; +import org.junit.jupiter.api.Test; + + + +/** + * AsyncTest + * + * Tests async handling wrt sessions. + */ +public class AsyncTest +{ + + public static class LatchServlet extends HttpServlet + { + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException + { + resp.getWriter().println("Latched"); + } + + } + + + /** + * Test async with a session. + * + * @throws Exception + */ + @Test + public void testSessionWithAsync() throws Exception + { + + + + DefaultSessionCacheFactory cacheFactory = new DefaultSessionCacheFactory(); + cacheFactory.setEvictionPolicy(SessionCache.EVICT_ON_SESSION_EXIT); + SessionDataStoreFactory storeFactory = new TestSessionDataStoreFactory(); + TestServer server1 = new TestServer(0, -1, -1, cacheFactory, storeFactory); + + String contextPath = ""; + String fooMapping = "/server"; + TestFooServlet servlet = new TestFooServlet(); + ServletHolder holder = new ServletHolder(servlet); + ServletContextHandler contextHandler = server1.addContext(contextPath); + contextHandler.addServlet(holder, fooMapping); + LatchServlet latchServlet = new LatchServlet(); + ServletHolder latchHolder = new ServletHolder(latchServlet); + contextHandler.addServlet(latchHolder, "/latch"); + + server1.start(); + int port1 = server1.getPort(); + + try (StacklessLogging stackless = new StacklessLogging(Log.getLogger("org.eclipse.jetty.server.session"))) + { + HttpClient client = new HttpClient(); + client.start(); + String url = "http://localhost:" + port1 + contextPath + fooMapping+"?action=async"; + + //make a request to set up a session on the server + ContentResponse response = client.GET(url); + assertEquals(HttpServletResponse.SC_OK,response.getStatus()); + + String sessionCookie = response.getHeaders().get("Set-Cookie"); + assertTrue(sessionCookie != null); + + //make another request, when this is handled, the first request is definitely finished being handled + response = client.GET("http://localhost:" + port1 + contextPath + "/latch"); + assertEquals(HttpServletResponse.SC_OK,response.getStatus()); + + //session should now be evicted from the cache after request exited + String id = TestServer.extractSessionId(sessionCookie); + assertFalse(contextHandler.getSessionHandler().getSessionCache().contains(id)); + assertTrue(contextHandler.getSessionHandler().getSessionCache().getSessionDataStore().exists(id)); + } + finally + { + server1.stop(); + } + } +}