diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/AsyncContextState.java b/jetty-server/src/main/java/org/eclipse/jetty/server/AsyncContextState.java index 4498db8f6e4..596d6055069 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/AsyncContextState.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/AsyncContextState.java @@ -58,32 +58,7 @@ public class AsyncContextState implements AsyncContext @Override public void addListener(final AsyncListener listener, final ServletRequest request, final ServletResponse response) { - AsyncListener wrap = new AsyncListener() - { - @Override - public void onTimeout(AsyncEvent event) throws IOException - { - listener.onTimeout(new AsyncEvent(event.getAsyncContext(),request,response,event.getThrowable())); - } - - @Override - public void onStartAsync(AsyncEvent event) throws IOException - { - listener.onStartAsync(new AsyncEvent(event.getAsyncContext(),request,response,event.getThrowable())); - } - - @Override - public void onError(AsyncEvent event) throws IOException - { - listener.onError(new AsyncEvent(event.getAsyncContext(),request,response,event.getThrowable())); - } - - @Override - public void onComplete(AsyncEvent event) throws IOException - { - listener.onComplete(new AsyncEvent(event.getAsyncContext(),request,response,event.getThrowable())); - } - }; + AsyncListener wrap = new WrappedAsyncListener(listener, request, response); state().addListener(wrap); } @@ -188,6 +163,46 @@ public class AsyncContextState implements AsyncContext return state(); } - + public static class WrappedAsyncListener implements AsyncListener + { + private final AsyncListener _listener; + private final ServletRequest _request; + private final ServletResponse _response; + public WrappedAsyncListener(AsyncListener listener, ServletRequest request, ServletResponse response) + { + _listener = listener; + _request = request; + _response = response; + } + + public AsyncListener getListener() + { + return _listener; + } + + @Override + public void onTimeout(AsyncEvent event) throws IOException + { + _listener.onTimeout(new AsyncEvent(event.getAsyncContext(), _request, _response,event.getThrowable())); + } + + @Override + public void onStartAsync(AsyncEvent event) throws IOException + { + _listener.onStartAsync(new AsyncEvent(event.getAsyncContext(), _request, _response,event.getThrowable())); + } + + @Override + public void onError(AsyncEvent event) throws IOException + { + _listener.onError(new AsyncEvent(event.getAsyncContext(), _request, _response,event.getThrowable())); + } + + @Override + public void onComplete(AsyncEvent event) throws IOException + { + _listener.onComplete(new AsyncEvent(event.getAsyncContext(), _request, _response,event.getThrowable())); + } + } } diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannelState.java b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannelState.java index cbe1c3934a6..78e009ea839 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannelState.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannelState.java @@ -149,6 +149,25 @@ public class HttpChannelState } } + public boolean hasListener(AsyncListener listener) + { + try(Locker.Lock lock= _locker.lock()) + { + if (_asyncListeners==null) + return false; + for (AsyncListener l : _asyncListeners) + { + if (l==listener) + return true; + + if (l instanceof AsyncContextState.WrappedAsyncListener && ((AsyncContextState.WrappedAsyncListener)l).getListener()==listener) + return true; + } + + return false; + } + } + public void setTimeout(long ms) { try(Locker.Lock lock= _locker.lock()) diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/DefaultSessionIdManager.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/DefaultSessionIdManager.java index b61af4cbef1..70770c6fa7a 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/session/DefaultSessionIdManager.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/DefaultSessionIdManager.java @@ -55,7 +55,7 @@ public class DefaultSessionIdManager extends ContainerLifeCycle implements Sessi { private final static Logger LOG = Log.getLogger("org.eclipse.jetty.server.session"); - private final static String __NEW_SESSION_ID="org.eclipse.jetty.server.newSessionId"; + public final static String __NEW_SESSION_ID="org.eclipse.jetty.server.newSessionId"; protected static final AtomicLong COUNTER = new AtomicLong(); diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/Session.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/Session.java index 673b400ecd4..cd0fe8c3d03 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/session/Session.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/Session.java @@ -1148,4 +1148,18 @@ public class Session implements SessionHandler.SessionIf return _resident; } + @Override + public String toString() + { + try (Lock lock = _lock.lock()) + { + return String.format("%s@%x{id=%s,x=%s,req=%d,res=%b}", + getClass().getSimpleName(), + hashCode(), + _sessionData.getId(), + _extendedId, + _requests, + _resident); + } + } } 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..0879636f0d8 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 @@ -162,8 +162,22 @@ public class SessionHandler extends ScopedHandler @Override public void onComplete(AsyncEvent event) throws IOException { - //An async request has completed, so we can complete the session - complete(Request.getBaseRequest(event.getAsyncContext().getRequest()).getSession(false)); + // An async request has completed, so we can complete the session, + // but we must locate the session instance for this context + Request request = Request.getBaseRequest(event.getAsyncContext().getRequest()); + HttpSession session = request.getSession(false); + String id; + if (session!=null) + id = session.getId(); + else + { + id = (String)request.getAttribute(DefaultSessionIdManager.__NEW_SESSION_ID); + if (id==null) + id = request.getRequestedSessionId(); + } + + if (id!=null) + complete(getSession(id)); } @Override @@ -407,9 +421,12 @@ public class SessionHandler extends ScopedHandler */ public void complete(HttpSession session) { + if (LOG.isDebugEnabled()) + LOG.debug("Complete called with session {}", session); + if (session == null) return; - + Session s = ((SessionIf)session).getSession(); try @@ -422,23 +439,26 @@ public class SessionHandler extends ScopedHandler LOG.warn(e); } } - - - public void complete (Session session, Request request) + + @Deprecated + public void complete(Session session, Request baseRequest) { - if (request.isAsyncStarted() && request.getDispatcherType() == DispatcherType.REQUEST) + ensureCompletion(baseRequest); + } + + private void ensureCompletion(Request baseRequest) + { + if (baseRequest.isAsyncStarted()) { - request.getAsyncContext().addListener(_sessionAsyncListener); + if (LOG.isDebugEnabled()) + LOG.debug("Adding AsyncListener for {}", baseRequest); + if (!baseRequest.getHttpChannelState().hasListener(_sessionAsyncListener)) + baseRequest.getAsyncContext().addListener(_sessionAsyncListener); } else { - complete(session); + complete(baseRequest.getSession(false)); } - //if dispatcher type is not async and not request, complete immediately (its a forward or an include) - - //else if dispatcher type is request and not async, complete immediately - - //else register an async callback completion listener that will complete the session } @@ -455,7 +475,6 @@ public class SessionHandler extends ScopedHandler _context=ContextHandler.getCurrentContext(); _loader=Thread.currentThread().getContextClassLoader(); - synchronized (server) { //Get a SessionDataStore and a SessionDataStore, falling back to in-memory sessions only @@ -472,7 +491,6 @@ public class SessionHandler extends ScopedHandler _sessionCache.setSessionDataStore(sds); } - if (_sessionIdManager==null) { @@ -1593,16 +1611,19 @@ public class SessionHandler extends ScopedHandler @Override public void doScope(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { - SessionHandler old_session_manager = null; + SessionHandler old_session_handler = null; HttpSession old_session = null; HttpSession existingSession = null; try { - old_session_manager = baseRequest.getSessionHandler(); + if (LOG.isDebugEnabled()) + LOG.debug("SessionHandler.doScope"); + + old_session_handler = baseRequest.getSessionHandler(); old_session = baseRequest.getSession(false); - if (old_session_manager != this) + if (old_session_handler != this) { // new session context baseRequest.setSessionHandler(this); @@ -1613,7 +1634,7 @@ public class SessionHandler extends ScopedHandler // access any existing session for this context existingSession = baseRequest.getSession(false); - if ((existingSession != null) && (old_session_manager != this)) + if ((existingSession != null) && (old_session_handler != this)) { HttpCookie cookie = access(existingSession,request.isSecure()); // Handle changed ID or max-age refresh, but only if this is not a redispatched request @@ -1622,10 +1643,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); @@ -1637,16 +1655,18 @@ public class SessionHandler extends ScopedHandler finally { //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_handler={}, this={}, calling complete={}", baseRequest.getSession(false), old_session_handler, this, (old_session_handler != this)); + + // If we are leaving the scope of this session handler, ensure the session is completed + if (old_session_handler != this) + ensureCompletion(baseRequest); + + // revert the session handler to the previous, unless it was null, in which case remember it as + // the first session handler encountered. + if (old_session_handler != null && old_session_handler != this) { - complete((Session)finalSession, baseRequest); - } - - if (old_session_manager != null && old_session_manager != this) - { - baseRequest.setSessionHandler(old_session_manager); + baseRequest.setSessionHandler(old_session_handler); baseRequest.setSession(old_session); } } diff --git a/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/AttributeNameTest.java b/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/AttributeNameTest.java index d06871b6caa..876bd7e134e 100644 --- a/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/AttributeNameTest.java +++ b/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/AttributeNameTest.java @@ -117,7 +117,7 @@ public class AttributeNameTest //Mangle the cookie, replacing Path with $Path, etc. sessionCookie = sessionCookie.replaceFirst("(\\W)(P|p)ath=","$1\\$Path="); - //Make a request to the 2nd server which will do a refresh, use TestFooServlet to ensure that the + //Make a request to the 2nd server which will do a refresh, use TestServlet to ensure that the //session attribute with dotted name is not removed Request request2 = client.newRequest("http://localhost:" + port2 + contextPath + servletMapping + "?action=get"); request2.header("Cookie", sessionCookie); 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 deleted file mode 100644 index 548a5498106..00000000000 --- a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/TestFooServlet.java +++ /dev/null @@ -1,59 +0,0 @@ -// -// ======================================================================== -// 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 java.io.IOException; -import java.lang.reflect.Proxy; - -import javax.servlet.ServletException; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import javax.servlet.http.HttpSession; - - - -public class TestFooServlet extends HttpServlet -{ - @Override - protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException - { - String action = request.getParameter("action"); - - if ("create".equals(action)) - { - HttpSession session = request.getSession(true); - TestFoo testFoo = new TestFoo(); - testFoo.setInt(33); - FooInvocationHandler handler = new FooInvocationHandler(testFoo); - Foo foo = (Foo)Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), new Class[] {Foo.class}, handler); - session.setAttribute("foo", foo); - } - else if ("test".equals(action)) - { - HttpSession session = request.getSession(false); - if (session == null) - response.sendError(500, "Session not activated"); - Foo foo = (Foo)session.getAttribute("foo"); - if (foo == null || foo.getInt() != 33) - response.sendError(500, "Foo not deserialized"); - } - - } -} 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..2c8a105d3fc --- /dev/null +++ b/tests/test-sessions/test-sessions-common/src/test/java/org/eclipse/jetty/server/session/AsyncTest.java @@ -0,0 +1,358 @@ +// +// ======================================================================== +// 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 java.lang.reflect.Proxy; + +import javax.servlet.AsyncContext; +import javax.servlet.ServletException; +import javax.servlet.ServletOutputStream; +import javax.servlet.WriteListener; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; + +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 + public void testSessionWithAsyncDispatch() throws Exception + { + // Test async dispatch back to same context, which then creates a session. + + DefaultSessionCacheFactory cacheFactory = new DefaultSessionCacheFactory(); + cacheFactory.setEvictionPolicy(SessionCache.EVICT_ON_SESSION_EXIT); + SessionDataStoreFactory storeFactory = new TestSessionDataStoreFactory(); + TestServer server = new TestServer(0, -1, -1, cacheFactory, storeFactory); + + String contextPath = ""; + String mapping = "/server"; + + ServletContextHandler contextHandler = server.addContext(contextPath); + TestServlet servlet = new TestServlet(); + ServletHolder holder = new ServletHolder(servlet); + contextHandler.addServlet(holder, mapping); + LatchServlet latchServlet = new LatchServlet(); + ServletHolder latchHolder = new ServletHolder(latchServlet); + contextHandler.addServlet(latchHolder, "/latch"); + + server.start(); + int port = server.getPort(); + + try (StacklessLogging stackless = new StacklessLogging(Log.getLogger("org.eclipse.jetty.server.session"))) + { + HttpClient client = new HttpClient(); + client.start(); + String url = "http://localhost:" + port + contextPath + mapping+"?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:" + port + 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 + { + server.stop(); + } + } + + @Test + public void testSessionWithAsyncComplete() throws Exception + { + // Test async write, which creates a session and completes outside of a dispatch + + DefaultSessionCacheFactory cacheFactory = new DefaultSessionCacheFactory(); + cacheFactory.setEvictionPolicy(SessionCache.EVICT_ON_SESSION_EXIT); + SessionDataStoreFactory storeFactory = new TestSessionDataStoreFactory(); + TestServer server = new TestServer(0, -1, -1, cacheFactory, storeFactory); + + String contextPath = ""; + String mapping = "/server"; + + ServletContextHandler contextHandler = server.addContext(contextPath); + TestServlet servlet = new TestServlet(); + ServletHolder holder = new ServletHolder(servlet); + contextHandler.addServlet(holder, mapping); + LatchServlet latchServlet = new LatchServlet(); + ServletHolder latchHolder = new ServletHolder(latchServlet); + contextHandler.addServlet(latchHolder, "/latch"); + + server.start(); + int port = server.getPort(); + + try (StacklessLogging stackless = new StacklessLogging(Log.getLogger("org.eclipse.jetty.server.session"))) + { + HttpClient client = new HttpClient(); + client.start(); + String url = "http://localhost:" + port + contextPath + mapping+"?action=asyncComplete"; + + //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:" + port + 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 + { + server.stop(); + } + } + + @Test + public void testSessionWithCrossContextAsync() throws Exception + { + // Test async dispatch from context A to context B then + // async dispatch back to context B, which then creates a session (in context B). + + DefaultSessionCacheFactory cacheFactory = new DefaultSessionCacheFactory(); + cacheFactory.setEvictionPolicy(SessionCache.EVICT_ON_SESSION_EXIT); + SessionDataStoreFactory storeFactory = new TestSessionDataStoreFactory(); + TestServer server = new TestServer(0, -1, -1, cacheFactory, storeFactory); + + ServletContextHandler contextA = server.addContext("/ctxA"); + CrossContextServlet ccServlet = new CrossContextServlet(); + ServletHolder ccHolder = new ServletHolder(ccServlet); + contextA.addServlet(ccHolder, "/*"); + + ServletContextHandler contextB = server.addContext("/ctxB"); + TestServlet testServlet = new TestServlet(); + ServletHolder testHolder = new ServletHolder(testServlet); + contextB.addServlet(testHolder, "/*"); + LatchServlet latchServlet = new LatchServlet(); + ServletHolder latchHolder = new ServletHolder(latchServlet); + contextB.addServlet(latchHolder, "/latch"); + + + server.start(); + int port = server.getPort(); + + try (StacklessLogging stackless = new StacklessLogging(Log.getLogger("org.eclipse.jetty.server.session"))) + { + HttpClient client = new HttpClient(); + client.start(); + String url = "http://localhost:" + port + "/ctxA/test?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:" + port + "/ctxB/latch"); + assertEquals(HttpServletResponse.SC_OK,response.getStatus()); + + //session should now be evicted from the cache after request exited + String id = TestServer.extractSessionId(sessionCookie); + assertFalse(contextB.getSessionHandler().getSessionCache().contains(id)); + assertTrue(contextB.getSessionHandler().getSessionCache().getSessionDataStore().exists(id)); + } + finally + { + server.stop(); + } + } + + @Test + public void testSessionWithCrossContextAsyncComplete() throws Exception + { + // Test async dispatch from context A to context B, which then does an + // async write, which creates a session (in context A) and completes outside of a + // dispatch + + DefaultSessionCacheFactory cacheFactory = new DefaultSessionCacheFactory(); + cacheFactory.setEvictionPolicy(SessionCache.EVICT_ON_SESSION_EXIT); + SessionDataStoreFactory storeFactory = new TestSessionDataStoreFactory(); + TestServer server = new TestServer(0, -1, -1, cacheFactory, storeFactory); + + ServletContextHandler contextA = server.addContext("/ctxA"); + CrossContextServlet ccServlet = new CrossContextServlet(); + ServletHolder ccHolder = new ServletHolder(ccServlet); + contextA.addServlet(ccHolder, "/*"); + + ServletContextHandler contextB = server.addContext("/ctxB"); + TestServlet testServlet = new TestServlet(); + ServletHolder testHolder = new ServletHolder(testServlet); + contextB.addServlet(testHolder, "/*"); + LatchServlet latchServlet = new LatchServlet(); + ServletHolder latchHolder = new ServletHolder(latchServlet); + contextB.addServlet(latchHolder, "/latch"); + + server.start(); + int port = server.getPort(); + + try (StacklessLogging stackless = new StacklessLogging(Log.getLogger("org.eclipse.jetty.server.session"))) + { + HttpClient client = new HttpClient(); + client.start(); + String url = "http://localhost:" + port + "/ctxA/test?action=asyncComplete"; + + //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:" + port + "/ctxB/latch"); + assertEquals(HttpServletResponse.SC_OK,response.getStatus()); + + //session should now be evicted from the cache A after request exited + String id = TestServer.extractSessionId(sessionCookie); + assertFalse(contextA.getSessionHandler().getSessionCache().contains(id)); + assertTrue(contextA.getSessionHandler().getSessionCache().getSessionDataStore().exists(id)); + } + finally + { + server.stop(); + } + } + + public static class TestServlet extends HttpServlet + { + @Override + protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException + { + String action = request.getParameter("action"); + + if ("create".equals(action)) + { + HttpSession session = request.getSession(true); + TestFoo testFoo = new TestFoo(); + testFoo.setInt(33); + FooInvocationHandler handler = new FooInvocationHandler(testFoo); + Foo foo = (Foo)Proxy + .newProxyInstance(Thread.currentThread().getContextClassLoader(), new Class[] {Foo.class}, handler); + session.setAttribute("foo", foo); + } + else if ("test".equals(action)) + { + HttpSession session = request.getSession(false); + if (session == null) + response.sendError(500, "Session not activated"); + Foo foo = (Foo)session.getAttribute("foo"); + 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(); + acontext.dispatch(); + return; + } + else + { + HttpSession session = request.getSession(true); + response.getWriter().println("OK"); + } + } + else if ("asyncComplete".equals(action)) + { + AsyncContext acontext = request.startAsync(); + ServletOutputStream out = response.getOutputStream(); + out.setWriteListener(new WriteListener() + { + @Override + public void onWritePossible() throws IOException + { + if (out.isReady()) + { + request.getSession(true); + out.print("OK\n"); + acontext.complete(); + } + } + + @Override + public void onError(Throwable t) + { + + } + }); + } + } + } + + public static class CrossContextServlet extends HttpServlet + { + @Override + protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException + { + AsyncContext acontext = request.startAsync(); + + acontext.dispatch(request.getServletContext().getContext("/ctxB"),"/test"); + } + } +} diff --git a/tests/test-sessions/test-sessions-common/src/test/java/org/eclipse/jetty/server/session/DeleteUnloadableSessionTest.java b/tests/test-sessions/test-sessions-common/src/test/java/org/eclipse/jetty/server/session/DeleteUnloadableSessionTest.java index 1565fb3f96b..be60e5f0824 100644 --- a/tests/test-sessions/test-sessions-common/src/test/java/org/eclipse/jetty/server/session/DeleteUnloadableSessionTest.java +++ b/tests/test-sessions/test-sessions-common/src/test/java/org/eclipse/jetty/server/session/DeleteUnloadableSessionTest.java @@ -128,12 +128,7 @@ public class DeleteUnloadableSessionTest } } - - /** - * TestFooServlet - * - * - */ + public static class TestServlet extends HttpServlet { private static final long serialVersionUID = 1L; diff --git a/tests/test-sessions/test-sessions-common/src/test/java/org/eclipse/jetty/server/session/SessionEvictionFailureTest.java b/tests/test-sessions/test-sessions-common/src/test/java/org/eclipse/jetty/server/session/SessionEvictionFailureTest.java index 27687b4c82a..214d3e59985 100644 --- a/tests/test-sessions/test-sessions-common/src/test/java/org/eclipse/jetty/server/session/SessionEvictionFailureTest.java +++ b/tests/test-sessions/test-sessions-common/src/test/java/org/eclipse/jetty/server/session/SessionEvictionFailureTest.java @@ -136,16 +136,9 @@ public class SessionEvictionFailureTest } - - - /** - * TestFooServlet - * - * - */ + public static class TestServlet extends HttpServlet { - @Override protected void doGet(HttpServletRequest request, HttpServletResponse httpServletResponse) throws ServletException, IOException {