From 6d9b36c8a4e3000d7241348fc32aaca9a7d4ebac Mon Sep 17 00:00:00 2001 From: Simone Bordet Date: Wed, 2 Mar 2016 18:01:32 +0100 Subject: [PATCH 1/6] Issue #381 (HttpClient does not send the Authorization header with authenticating proxy) Fixed by tracking correctly the conversation attributes for authentication, and by applying both proxy authentication results and server authentication results. --- .../client/AuthenticationProtocolHandler.java | 18 ++--- .../eclipse/jetty/client/HttpConnection.java | 21 +++-- .../ProxyAuthenticationProtocolHandler.java | 7 ++ .../WWWAuthenticationProtocolHandler.java | 7 ++ .../jetty/client/HttpClientProxyTest.java | 76 ++++++++++++++++++- 5 files changed, 108 insertions(+), 21 deletions(-) diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/AuthenticationProtocolHandler.java b/jetty-client/src/main/java/org/eclipse/jetty/client/AuthenticationProtocolHandler.java index 9d7c297774e..860cdd9e26f 100644 --- a/jetty-client/src/main/java/org/eclipse/jetty/client/AuthenticationProtocolHandler.java +++ b/jetty-client/src/main/java/org/eclipse/jetty/client/AuthenticationProtocolHandler.java @@ -40,7 +40,6 @@ public abstract class AuthenticationProtocolHandler implements ProtocolHandler public static final int DEFAULT_MAX_CONTENT_LENGTH = 16*1024; public static final Logger LOG = Log.getLogger(AuthenticationProtocolHandler.class); private static final Pattern AUTHENTICATE_PATTERN = Pattern.compile("([^\\s]+)\\s+realm=\"([^\"]+)\"(.*)", Pattern.CASE_INSENSITIVE); - private static final String AUTHENTICATION_ATTRIBUTE = AuthenticationProtocolHandler.class.getName() + ".authentication"; private final HttpClient client; private final int maxContentLength; @@ -64,6 +63,8 @@ public abstract class AuthenticationProtocolHandler implements ProtocolHandler protected abstract URI getAuthenticationURI(Request request); + protected abstract String getAuthenticationAttribute(); + @Override public Response.Listener getResponseListener() { @@ -92,8 +93,9 @@ public abstract class AuthenticationProtocolHandler implements ProtocolHandler return; } + String authenticationAttribute = getAuthenticationAttribute(); HttpConversation conversation = request.getConversation(); - if (conversation.getAttribute(AUTHENTICATION_ATTRIBUTE) != null) + if (conversation.getAttribute(authenticationAttribute) != null) { // We have already tried to authenticate, but we failed again if (LOG.isDebugEnabled()) @@ -146,18 +148,12 @@ public abstract class AuthenticationProtocolHandler implements ProtocolHandler return; } - conversation.setAttribute(AUTHENTICATION_ATTRIBUTE, true); + conversation.setAttribute(authenticationAttribute, true); Request newRequest = client.copyRequest(request, request.getURI()); authnResult.apply(newRequest); - newRequest.onResponseSuccess(new Response.SuccessListener() - { - @Override - public void onSuccess(Response response) - { - client.getAuthenticationStore().addAuthenticationResult(authnResult); - } - }).send(null); + newRequest.onResponseSuccess(r -> client.getAuthenticationStore().addAuthenticationResult(authnResult)) + .send(null); } catch (Throwable x) { diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpConnection.java b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpConnection.java index aa50e988138..4c9d4ed3590 100644 --- a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpConnection.java +++ b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpConnection.java @@ -153,14 +153,9 @@ public abstract class HttpConnection implements Connection request.header(HttpHeader.COOKIE.asString(), cookies.toString()); } - // Authorization - URI authenticationURI = proxy != null ? proxy.getURI() : request.getURI(); - if (authenticationURI != null) - { - Authentication.Result authnResult = getHttpClient().getAuthenticationStore().findAuthenticationResult(authenticationURI); - if (authnResult != null) - authnResult.apply(request); - } + // Authentication + applyAuthentication(request, proxy != null ? proxy.getURI() : null); + applyAuthentication(request, request.getURI()); } private StringBuilder convertCookies(List cookies, StringBuilder builder) @@ -177,6 +172,16 @@ public abstract class HttpConnection implements Connection return builder; } + private void applyAuthentication(Request request, URI uri) + { + if (uri != null) + { + Authentication.Result result = getHttpClient().getAuthenticationStore().findAuthenticationResult(uri); + if (result != null) + result.apply(request); + } + } + protected SendFailure send(HttpChannel channel, HttpExchange exchange) { // Forbid idle timeouts for the time window where diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/ProxyAuthenticationProtocolHandler.java b/jetty-client/src/main/java/org/eclipse/jetty/client/ProxyAuthenticationProtocolHandler.java index 4099974417c..85f2c30919d 100644 --- a/jetty-client/src/main/java/org/eclipse/jetty/client/ProxyAuthenticationProtocolHandler.java +++ b/jetty-client/src/main/java/org/eclipse/jetty/client/ProxyAuthenticationProtocolHandler.java @@ -34,6 +34,7 @@ import org.eclipse.jetty.http.HttpStatus; public class ProxyAuthenticationProtocolHandler extends AuthenticationProtocolHandler { public static final String NAME = "proxy-authenticate"; + private static final String ATTRIBUTE = ProxyAuthenticationProtocolHandler.class.getName() + ".attribute"; public ProxyAuthenticationProtocolHandler(HttpClient client) { @@ -76,4 +77,10 @@ public class ProxyAuthenticationProtocolHandler extends AuthenticationProtocolHa ProxyConfiguration.Proxy proxy = destination.getProxy(); return proxy != null ? proxy.getURI() : request.getURI(); } + + @Override + protected String getAuthenticationAttribute() + { + return ATTRIBUTE; + } } diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/WWWAuthenticationProtocolHandler.java b/jetty-client/src/main/java/org/eclipse/jetty/client/WWWAuthenticationProtocolHandler.java index 5f023a95d07..3d0ab9d849d 100644 --- a/jetty-client/src/main/java/org/eclipse/jetty/client/WWWAuthenticationProtocolHandler.java +++ b/jetty-client/src/main/java/org/eclipse/jetty/client/WWWAuthenticationProtocolHandler.java @@ -34,6 +34,7 @@ import org.eclipse.jetty.http.HttpStatus; public class WWWAuthenticationProtocolHandler extends AuthenticationProtocolHandler { public static final String NAME = "www-authenticate"; + private static final String ATTRIBUTE = WWWAuthenticationProtocolHandler.class.getName() + ".attribute"; public WWWAuthenticationProtocolHandler(HttpClient client) { @@ -74,4 +75,10 @@ public class WWWAuthenticationProtocolHandler extends AuthenticationProtocolHand { return request.getURI(); } + + @Override + protected String getAuthenticationAttribute() + { + return ATTRIBUTE; + } } diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientProxyTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientProxyTest.java index 8a8866c2d1e..2efc6c6bf25 100644 --- a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientProxyTest.java +++ b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientProxyTest.java @@ -81,7 +81,7 @@ public class HttpClientProxyTest extends AbstractHttpClientServerTest } @Test - public void testAuthenticatedProxiedRequest() throws Exception + public void testProxyAuthentication() throws Exception { final String user = "foo"; final String password = "bar"; @@ -160,7 +160,7 @@ public class HttpClientProxyTest extends AbstractHttpClientServerTest } @Test - public void testAuthenticatedProxiedRequestWithRedirect() throws Exception + public void testProxyAuthenticationWithRedirect() throws Exception { String user = "foo"; String password = "bar"; @@ -254,4 +254,76 @@ public class HttpClientProxyTest extends AbstractHttpClientServerTest Assert.assertEquals(status, response3.getStatus()); Assert.assertEquals(1, requests.get()); } + + @Test + public void testProxyAuthenticationWithServerAuthentication() throws Exception + { + String proxyRealm = "proxyRealm"; + String serverRealm = "serverRealm"; + int status = HttpStatus.NO_CONTENT_204; + start(new AbstractHandler() + { + @Override + public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException + { + baseRequest.setHandled(true); + String authorization = request.getHeader(HttpHeader.PROXY_AUTHORIZATION.asString()); + if (authorization == null) + { + response.setStatus(HttpStatus.PROXY_AUTHENTICATION_REQUIRED_407); + response.setHeader(HttpHeader.PROXY_AUTHENTICATE.asString(), "Basic realm=\"" + proxyRealm + "\""); + } + else + { + authorization = request.getHeader(HttpHeader.AUTHORIZATION.asString()); + if (authorization == null) + { + response.setStatus(HttpStatus.UNAUTHORIZED_401); + response.setHeader(HttpHeader.WWW_AUTHENTICATE.asString(), "Basic realm=\"" + serverRealm + "\""); + } + else + { + response.setStatus(status); + } + } + } + }); + + String proxyHost = "localhost"; + int proxyPort = connector.getLocalPort(); + String serverHost = "server"; + int serverPort = proxyPort + 1; + URI proxyURI = URI.create(scheme + "://" + proxyHost + ":" + proxyPort); + client.getAuthenticationStore().addAuthentication(new BasicAuthentication(proxyURI, proxyRealm, "proxyUser", "proxyPassword")); + URI serverURI = URI.create(scheme + "://" + serverHost + ":" + serverPort); + client.getAuthenticationStore().addAuthentication(new BasicAuthentication(serverURI, serverRealm, "serverUser", "serverPassword")); + client.getProxyConfiguration().getProxies().add(new HttpProxy(proxyHost, proxyPort)); + final AtomicInteger requests = new AtomicInteger(); + client.getRequestListeners().add(new Request.Listener.Adapter() + { + @Override + public void onSuccess(Request request) + { + requests.incrementAndGet(); + } + }); + // Make a request, expect 407 + 401 + 204. + ContentResponse response1 = client.newRequest(serverHost, serverPort) + .scheme(scheme) + .timeout(5, TimeUnit.SECONDS) + .send(); + + Assert.assertEquals(status, response1.getStatus()); + Assert.assertEquals(3, requests.get()); + + // Make again the request, authentication is cached, expect 204. + requests.set(0); + ContentResponse response2 = client.newRequest(serverHost, serverPort) + .scheme(scheme) + .timeout(5, TimeUnit.SECONDS) + .send(); + + Assert.assertEquals(status, response2.getStatus()); + Assert.assertEquals(1, requests.get()); + } } From ff49714d06280c9fc161755e384cab897dc41698 Mon Sep 17 00:00:00 2001 From: Jan Bartel Date: Thu, 3 Mar 2016 00:37:48 +0100 Subject: [PATCH 2/6] Implement session idle and expiration with single periodic sweep. --- .../InfinispanSessionDataStore.java | 2 +- .../server/session/AbstractSessionStore.java | 299 +++++++++++------- .../server/session/FileSessionManager.java | 11 +- .../jetty/server/session/IdleInspector.java | 5 + .../server/session/JDBCSessionDataStore.java | 1 - .../server/session/MemorySessionStore.java | 37 +-- .../eclipse/jetty/server/session/Session.java | 129 ++++++-- .../jetty/server/session/SessionManager.java | 9 +- .../server/session/SessionCookieTest.java | 10 + .../jetty/server/session/FileTestServer.java | 69 ++++ .../jetty/server/session/IdleSessionTest.java | 107 +++++++ .../gcloud/session/GCloudTestServer.java | 4 - .../session/InvalidationSessionTest.java | 36 ++- .../gcloud/session/SessionExpiryTest.java | 8 +- .../jetty/server/session/IdleSessionTest.java | 47 +-- .../server/session/OrphanedSessionTest.java | 38 --- .../session/InfinispanTestSessionServer.java | 3 - .../session/InvalidationSessionTest.java | 91 ------ .../remote/RemoteInvalidationSessionTest.java | 31 +- .../session/InvalidationSessionTest.java | 24 +- .../jetty/server/session/JdbcTestServer.java | 5 +- .../ReloadedSessionMissingClassTest.java | 5 +- .../server/session/SaveIntervalTest.java | 6 +- .../jetty/nosql/mongodb/IdleSessionTest.java | 111 +++++++ .../nosql/mongodb/InvalidateSessionTest.java | 26 +- .../jetty/nosql/mongodb/MongoTestServer.java | 8 +- .../session/AbstractIdleSessionTest.java | 261 +++++++++++++++ .../AbstractInvalidationSessionTest.java | 6 +- .../server/session/AbstractTestServer.java | 21 +- 29 files changed, 1043 insertions(+), 367 deletions(-) create mode 100644 tests/test-sessions/test-file-sessions/src/test/java/org/eclipse/jetty/server/session/IdleSessionTest.java delete mode 100644 tests/test-sessions/test-hash-sessions/src/test/java/org/eclipse/jetty/server/session/OrphanedSessionTest.java delete mode 100644 tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/InvalidationSessionTest.java create mode 100644 tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/IdleSessionTest.java create mode 100644 tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractIdleSessionTest.java diff --git a/jetty-infinispan/src/main/java/org/eclipse/jetty/session/infinispan/InfinispanSessionDataStore.java b/jetty-infinispan/src/main/java/org/eclipse/jetty/session/infinispan/InfinispanSessionDataStore.java index 5457845b854..e5f71ac5603 100644 --- a/jetty-infinispan/src/main/java/org/eclipse/jetty/session/infinispan/InfinispanSessionDataStore.java +++ b/jetty-infinispan/src/main/java/org/eclipse/jetty/session/infinispan/InfinispanSessionDataStore.java @@ -208,7 +208,7 @@ public class InfinispanSessionDataStore extends AbstractSessionDataStore try { Class remoteClass = Thread.currentThread().getContextClassLoader().loadClass("org.infinispan.client.hotrod.RemoteCache"); - if (_cache.getClass().isAssignableFrom(remoteClass)) + if (remoteClass.isAssignableFrom(_cache.getClass())) { return true; } diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSessionStore.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSessionStore.java index 8176504628d..f734d761f6d 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSessionStore.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSessionStore.java @@ -44,7 +44,6 @@ public abstract class AbstractSessionStore extends AbstractLifeCycle implements final static Logger LOG = Log.getLogger("org.eclipse.jetty.server.session"); protected SessionDataStore _sessionDataStore; - protected StalenessStrategy _staleStrategy; protected SessionManager _manager; protected SessionContext _context; protected int _idlePassivationTimeoutSec; @@ -80,6 +79,16 @@ public abstract class AbstractSessionStore extends AbstractLifeCycle implements public abstract Session doPutIfAbsent (String id, Session session); + /** + * Replace the mapping from id to oldValue with newValue + * @param id + * @param oldValue + * @param newValue + * @return true if replacement was done + */ + public abstract boolean doReplace (String id, Session oldValue, Session newValue); + + /** * Check to see if the session exists in the store @@ -99,6 +108,24 @@ public abstract class AbstractSessionStore extends AbstractLifeCycle implements + /** + * PlaceHolder + * + * + */ + protected class PlaceHolderSession extends Session + { + + /** + * @param data + */ + public PlaceHolderSession(SessionData data) + { + super(data); + } + } + + /** * @@ -185,22 +212,7 @@ public abstract class AbstractSessionStore extends AbstractLifeCycle implements _sessionDataStore = sessionDataStore; } - /** - * @return the strategy for detecting stale sessions or null if there isn't one - */ - public StalenessStrategy getStaleStrategy() - { - return _staleStrategy; - } - - /** - * @param staleStrategy - */ - public void setStaleStrategy(StalenessStrategy staleStrategy) - { - _staleStrategy = staleStrategy; - } - + /** * @see org.eclipse.jetty.server.session.SessionStore#getIdlePassivationTimeoutSec() @@ -238,53 +250,132 @@ public abstract class AbstractSessionStore extends AbstractLifeCycle implements @Override public Session get(String id, boolean staleCheck) throws Exception { - //look locally - Session session = doGet(id); + Session session = null; + Exception ex = null; - //TODO also check that session is only written out if only the access time changes infrequently - - //session is either not in session store, or it is stale, or its been passivated, load the data for the session if possible - if (session == null || (staleCheck && isStale(session)) || session.isPassivated() && _sessionDataStore != null) + while (true) { - SessionData data = _sessionDataStore.load(id); - - //session wasn't in session store + session = doGet(id); + + if (_sessionDataStore == null) + break; //can't load any session data so just return null or the session object + if (session == null) { - if (data != null) + if (LOG.isDebugEnabled()) + LOG.debug("Session not found locally, attempting to load"); + + //didn't get a session, try and create one and put in a placeholder for it + PlaceHolderSession phs = new PlaceHolderSession (new SessionData(id, null, null,0,0,0,0)); + Lock phsLock = phs.lock(); + Session s = doPutIfAbsent(id, phs); + if (s == null) { - session = newSession(data); - session.setSessionManager(_manager); - Session existing = doPutIfAbsent(id, session); - if (existing != null) + //My placeholder won, go ahead and load the full session data + try { - //some other thread has got in first and added the session - //so use it - session = existing; + session = loadSession(id); + if (session == null) + { + //session does not exist, remove the placeholder + doDelete(id); + phsLock.close(); + break; + } + + try (Lock lock = session.lock()) + { + //swap it in instead of the placeholder + boolean success = doReplace(id, phs, session); + if (!success) + { + //something has gone wrong, it should have been our placeholder + doDelete(id); + session = null; + LOG.warn("Replacement of placeholder for session {} failed", id); + phsLock.close(); + break; + } + + //successfully swapped in the session + phsLock.close(); + break; + } + } + catch (Exception e) + { + ex = e; //remember a problem happened loading the session + LOG.warn(e); + doDelete(id); //remove the placeholder + phsLock.close(); + session = null; + break; } } - //else session not in store and not in data store either, so doesn't exist + else + { + //my placeholder didn't win, check the session returned + phsLock.close(); + try (Lock lock = s.lock()) + { + //is it a placeholder? or is it passivated? In both cases, chuck it away and start again + if (s.isPassivated() || s instanceof PlaceHolderSession) + { + session = null; + continue; + } + session = s; + break; + } + } + } else { - //session was already in session store, refresh it if its still stale/passivated + //check the session returned try (Lock lock = session.lock()) - { - if (session.isPassivated() || staleCheck && isStale(session)) + { + //is it a placeholder? or is it passivated? In both cases, chuck it away and start again + if (session.isPassivated() || session instanceof PlaceHolderSession) { - //if we were able to load it, then update our session object - if (data != null) - { - session.setPassivated(false); - session.getSessionData().copy(data); - session.didActivate(); - } - else - session = null; //TODO rely on the expiry mechanism to get rid of it? + session = null; + continue; } + + //got the session + break; } } } + + if (ex != null) + throw ex; + return session; + } + + /** + * Load the info for the session from the session data store + * + * @param id + * @return a Session object filled with data or null if the session doesn't exist + * @throws Exception + */ + private Session loadSession (String id) + throws Exception + { + SessionData data = null; + Session session = null; + + if (_sessionDataStore == null) + return null; //can't load it + + data =_sessionDataStore.load(id); + + if (data == null) //session doesn't exist + return null; + + session = newSession(data); + session.setSessionManager(_manager); return session; } @@ -300,13 +391,21 @@ public abstract class AbstractSessionStore extends AbstractLifeCycle implements { if (id == null || session == null) throw new IllegalArgumentException ("Put key="+id+" session="+(session==null?"null":session.getId())); - - session.setSessionManager(_manager); + + //if the session is new, the data has changed, or the cache is considered stale, write it to any backing store try (Lock lock = session.lock()) { - if ((session.isNew() || session.getSessionData().isDirty() || isStale(session)) && _sessionDataStore != null) + session.setSessionManager(_manager); + + if (session.isPassivated()) + throw new IllegalStateException ("Session "+id+" is passivated and cannot be saved"); + + if (!session.isValid()) + return; + + if ((session.isNew() || session.getSessionData().isDirty()) && _sessionDataStore != null) { if (_sessionDataStore.isPassivating()) { @@ -322,11 +421,11 @@ public abstract class AbstractSessionStore extends AbstractLifeCycle implements } else _sessionDataStore.store(id, session.getSessionData()); + + } - + doPutIfAbsent(id,session); } - - doPutIfAbsent(id,session); } /** @@ -344,60 +443,28 @@ public abstract class AbstractSessionStore extends AbstractLifeCycle implements /** * Remove a session object from this store and from any backing store. * - * If session has been passivated, may need to reload it before it can - * be properly deleted * * @see org.eclipse.jetty.server.session.SessionStore#delete(java.lang.String) */ @Override public Session delete(String id) throws Exception - { - //Ensure that the session object is not passivated so that its attributes - //are valid - Session session = doGet(id); - - //TODO if (session == null) do we want to load it to delete it? - if (session != null) - { - try (Lock lock = session.lock()) - { - //TODO don't check stale on deletion? - if (session.isPassivated() && _sessionDataStore != null) - { - session.setPassivated(false); - SessionData data = _sessionDataStore.load(id); - if (data != null) - { - session.getSessionData().copy(data); - session.didActivate(); - } - } - } - } - - - //Always delete it from the data store + { + //get the session, if its not in memory, this will load it + Session session = get(id, false); + + //Always delete it from the backing data store if (_sessionDataStore != null) { boolean dsdel = _sessionDataStore.delete(id); if (LOG.isDebugEnabled()) LOG.debug("Session {} deleted in db {}",id, dsdel); } + + //delete it from the session object store return doDelete(id); } - - /** - * @param session - * @return true or false according to the StaleStrategy - */ - public boolean isStale (Session session) - { - if (_staleStrategy != null) - return _staleStrategy.isStale(session); - return false; - } - + @@ -443,8 +510,8 @@ public abstract class AbstractSessionStore extends AbstractLifeCycle implements /** - * If the SessionDataStore supports passivation, passivate any - * sessions that have not be accessed for longer than x sec + * If the SessionDataStore supports passivation, + * write the session to the backing data store. * * @param id identity of session to passivate */ @@ -453,12 +520,8 @@ public abstract class AbstractSessionStore extends AbstractLifeCycle implements if (!isStarted()) return; - if (_sessionDataStore == null) - return; //no data store to passivate - - if (!_sessionDataStore.isPassivating()) - return; //doesn't support passivation - + if (_sessionDataStore == null || !_sessionDataStore.isPassivating()) + return; //no data store to passivate or it doesn't passivate //get the session locally Session s = doGet(id); @@ -470,22 +533,34 @@ public abstract class AbstractSessionStore extends AbstractLifeCycle implements } + //lock the session during passivation try (Lock lock = s.lock()) { - //check the session is still idle first - if (s.isValid() && s.isIdleLongerThan(_idlePassivationTimeoutSec)) + //check the session is still idle and that it doesn't have requests using it + if (s.isValid() && s.isIdleLongerThan(_idlePassivationTimeoutSec) && s.isActive() && (s.getRequests() <= 0)) { - s.willPassivate(); - _sessionDataStore.store(id, s.getSessionData()); - s.getSessionData().clearAllAttributes(); - s.getSessionData().setDirty(false); + //TODO - do we need to check that the session exists in the session data store + //before we passivate it? If it doesn't exist, we can assume another node + //invalidated it. If the session was new, it shouldn't have been idle passivated. + try + { + if (LOG.isDebugEnabled()) + LOG.debug("Passivating idle session {}", id); + s.willPassivate(); + _sessionDataStore.store(id, s.getSessionData()); + s.getSessionData().setDirty(false); + s.setPassivated(); + doDelete(id); //Take the session object of this session store + } + catch (Exception e) + { + LOG.warn("Passivation of idle session {} failed", id, e); + s.setPassivated(); //set it as passivated so it can't be used + doDelete(id); //detach it + } } } - catch (Exception e) - { - LOG.warn("Passivation of idle session {} failed", id, e); - // TODO should do session.invalidate(); ??? - } + } diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/FileSessionManager.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/FileSessionManager.java index f79ee8fdc3c..090841b5c18 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/session/FileSessionManager.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/FileSessionManager.java @@ -27,13 +27,20 @@ package org.eclipse.jetty.server.session; */ public class FileSessionManager extends SessionManager { - protected FileSessionDataStore _sessionDataStore = new FileSessionDataStore(); + protected FileSessionDataStore _sessionDataStore; + /** + * + */ + public FileSessionManager () + { + _sessionStore = new MemorySessionStore(); + _sessionDataStore = new FileSessionDataStore(); + } @Override public void doStart() throws Exception { - _sessionStore = new MemorySessionStore(); ((AbstractSessionStore)_sessionStore).setSessionDataStore(_sessionDataStore); super.doStart(); diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/IdleInspector.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/IdleInspector.java index ca0d7979c02..33afe8c12b2 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/session/IdleInspector.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/IdleInspector.java @@ -81,6 +81,8 @@ public class IdleInspector implements SessionInspector public void preInspection() { _idleCandidates = new HashSet(); + if (LOG.isDebugEnabled()) + LOG.debug("IdleInspector preinspection"); } @@ -91,6 +93,9 @@ public class IdleInspector implements SessionInspector @Override public void postInspection() { + if (LOG.isDebugEnabled()) + LOG.debug("IdleInspector postinspection"); + for (String id:_idleCandidates) { try diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/JDBCSessionDataStore.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/JDBCSessionDataStore.java index ad029ec9215..6adcf5f5892 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/session/JDBCSessionDataStore.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/JDBCSessionDataStore.java @@ -717,7 +717,6 @@ public class JDBCSessionDataStore extends AbstractSessionDataStore //ensure this runs with context classloader set _context.run(r); - if (exception.get() != null) throw exception.get(); diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/MemorySessionStore.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/MemorySessionStore.java index 6ecf9021fdb..046f228d8ee 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/session/MemorySessionStore.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/MemorySessionStore.java @@ -124,7 +124,7 @@ public class MemorySessionStore extends AbstractSessionStore public Session doPutIfAbsent(String id, Session session) { Session s = _sessions.putIfAbsent(id, session); - if (s == null) + if (s == null && !(session instanceof PlaceHolderSession)) _stats.increment(); return s; } @@ -145,31 +145,11 @@ public class MemorySessionStore extends AbstractSessionStore public Session doDelete(String id) { Session s = _sessions.remove(id); - if (s != null) + if (s != null && !(s instanceof PlaceHolderSession)) _stats.decrement(); return s; } - -/* - - @Override - public Set doGetExpiredCandidates() - { - Set candidates = new HashSet(); - long now = System.currentTimeMillis(); - - for (Session s:_sessions.values()) - { - if (s.isExpiredAt(now)) - { - candidates.add(s.getId()); - } - } - return candidates; - } -*/ - @@ -253,4 +233,17 @@ public class MemorySessionStore extends AbstractSessionStore } + /** + * @see org.eclipse.jetty.server.session.AbstractSessionStore#doReplace(java.lang.String, org.eclipse.jetty.server.session.Session, org.eclipse.jetty.server.session.Session) + */ + @Override + public boolean doReplace(String id, Session oldValue, Session newValue) + { + boolean result = _sessions.replace(id, oldValue, newValue); + if (result && (oldValue instanceof PlaceHolderSession)) + _stats.increment(); + return result; + } + + } 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 d23d627c76a..751f8193823 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 @@ -51,11 +51,27 @@ public class Session implements SessionManager.SessionIf private final static Logger LOG = Log.getLogger("org.eclipse.jetty.server.session"); + /** + * + */ public final static String SESSION_CREATED_SECURE="org.eclipse.jetty.security.sessionCreatedSecure"; + /** + * State + * + * Validity states of a session + */ public enum State {VALID, INVALID, INVALIDATING}; - + + + /** + * PassivationState + * + * States of a session - either active in memory or passivated to persistent store + */ + public enum PassivationState {PASSIVATED, ACTIVE}; + protected SessionData _sessionData; protected SessionManager _manager; @@ -66,24 +82,52 @@ public class Session implements SessionManager.SessionIf private State _state = State.VALID; //state of the session:valid,invalid or being invalidated private Locker _lock = new Locker(); + private PassivationState _passivationState = PassivationState.ACTIVE; + + - private boolean _isPassivated; + /** + * Create a new session + * + * @param request + * @param data + */ public Session (HttpServletRequest request, SessionData data) { _sessionData = data; _newSession = true; - _requests = 1; + _requests = 1; //access will not be called on this new session, but we are obviously in a request } + + /** + * Re-create an existing session + * @param data + */ public Session (SessionData data) { _sessionData = data; - _requests = 1; } + /** + * Should call this method with a lock held if you want to + * make decision on what to do with the session + * + * @return + */ + public long getRequests() + { + try (Lock lock = _lock.lockIfNotHeld()) + { + return _requests; + } + } + + + public void setSessionManager (SessionManager manager) { _manager = manager; @@ -98,7 +142,7 @@ public class Session implements SessionManager.SessionIf /* ------------------------------------------------------------- */ protected void cookieSet() { - try (Lock lock = lock()) + try (Lock lock = _lock.lockIfNotHeld()) { _sessionData.setCookieSet(_sessionData.getAccessed()); } @@ -106,7 +150,7 @@ public class Session implements SessionManager.SessionIf /* ------------------------------------------------------------ */ protected boolean access(long time) { - try (Lock lock=lock()) + try (Lock lock = _lock.lockIfNotHeld()) { if (!isValid()) return false; @@ -129,7 +173,7 @@ public class Session implements SessionManager.SessionIf /* ------------------------------------------------------------ */ protected void complete() { - try (Lock lock = lock()) + try (Lock lock = _lock.lockIfNotHeld()) { _requests--; } @@ -363,7 +407,7 @@ public class Session implements SessionManager.SessionIf @Override public void setMaxInactiveInterval(int secs) { - try (Lock lock = lock()) + try (Lock lock = _lock.lockIfNotHeld()) { _sessionData.setMaxInactiveMs((long)secs*1000L); _sessionData.setExpiry(_sessionData.getMaxInactiveMs() <= 0 ? 0 : (System.currentTimeMillis() + _sessionData.getMaxInactiveMs()*1000L)); @@ -407,11 +451,13 @@ public class Session implements SessionManager.SessionIf */ protected void checkValidForWrite() throws IllegalStateException { - if (!_lock.isLocked()) - throw new IllegalStateException(); + checkLocked(); if (_state != State.VALID) throw new IllegalStateException(); + + if (_passivationState == PassivationState.PASSIVATED) + throw new IllegalStateException("Passivated"); } @@ -422,14 +468,26 @@ public class Session implements SessionManager.SessionIf */ protected void checkValidForRead () throws IllegalStateException { - if (!_lock.isLocked()) - throw new IllegalStateException(); + checkLocked(); + if (_state == State.INVALID) - throw new IllegalStateException(); + throw new IllegalStateException("Invalid"); + + if (_passivationState == PassivationState.PASSIVATED) + throw new IllegalStateException("Passivated"); } - + /* ------------------------------------------------------------- */ + /** + * @throws IllegalStateException + */ + protected void checkLocked () + throws IllegalStateException + { + if (!_lock.isLocked()) + throw new IllegalStateException("Session not locked"); + } /** * @see javax.servlet.http.HttpSession#getAttribute(java.lang.String) @@ -750,7 +808,7 @@ public class Session implements SessionManager.SessionIf /* ------------------------------------------------------------- */ public void setIdChanged(boolean changed) { - try (Lock lock = lock()) + try (Lock lock = _lock.lockIfNotHeld()) { _idChanged=changed; } @@ -784,25 +842,46 @@ public class Session implements SessionManager.SessionIf return _sessionData; } + + + - /* ------------------------------------------------------------- */ + /** + * + */ + public void setPassivated () + { + checkLocked(); + _passivationState = PassivationState.PASSIVATED; + } + + /** + * + */ + public void setActive () + { + checkLocked(); + _passivationState = PassivationState.ACTIVE; + } + + /** * @return */ - public boolean isPassivated() + public boolean isActive () { - return _isPassivated; + checkLocked(); + return _passivationState == PassivationState.ACTIVE; } - /* ------------------------------------------------------------- */ + + /** - * @param isPassivated + * @return */ - public void setPassivated(boolean isPassivated) + public boolean isPassivated () { - _isPassivated = isPassivated; + checkLocked(); + return _passivationState == PassivationState.PASSIVATED; } - - - } diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/SessionManager.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/SessionManager.java index 746a90f0f20..68d49b040f2 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/session/SessionManager.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/SessionManager.java @@ -53,6 +53,7 @@ import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.util.statistic.CounterStatistic; import org.eclipse.jetty.util.statistic.SampleStatistic; +import org.eclipse.jetty.util.thread.Locker.Lock; @@ -221,11 +222,11 @@ public class SessionManager extends ContainerLifeCycle implements org.eclipse.je public void complete(HttpSession session) { Session s = ((SessionIf)session).getSession(); - s.complete(); + try { - if (s.isValid()) - _sessionStore.put(s.getId(), s); + s.complete(); + _sessionStore.put(s.getId(), s); } catch (Exception e) { @@ -574,6 +575,7 @@ public class SessionManager extends ContainerLifeCycle implements org.eclipse.je @Override public HttpSession newHttpSession(HttpServletRequest request) { + long created=System.currentTimeMillis(); String id =_sessionIdManager.newSessionId(request,created); Session session = _sessionStore.newSession(request, id, created, (_dftMaxIdleSecs>0?_dftMaxIdleSecs*1000L:-1)); @@ -583,7 +585,6 @@ public class SessionManager extends ContainerLifeCycle implements org.eclipse.je session.getSessionData().setExpiry(_dftMaxIdleSecs <= 0 ? 0 : (created + _dftMaxIdleSecs*1000L)); if (request.isSecure()) session.setAttribute(Session.SESSION_CREATED_SECURE, Boolean.TRUE); - try { _sessionStore.put(id, session); diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/session/SessionCookieTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/session/SessionCookieTest.java index b6d83e9ae1c..e9b6b82039d 100644 --- a/jetty-server/src/test/java/org/eclipse/jetty/server/session/SessionCookieTest.java +++ b/jetty-server/src/test/java/org/eclipse/jetty/server/session/SessionCookieTest.java @@ -123,6 +123,16 @@ public class SessionCookieTest return null; } + /** + * @see org.eclipse.jetty.server.session.AbstractSessionStore#doReplace(java.lang.String, org.eclipse.jetty.server.session.Session, org.eclipse.jetty.server.session.Session) + */ + @Override + public boolean doReplace(String id, Session oldValue, Session newValue) + { + // TODO Auto-generated method stub + return false; + } + } diff --git a/tests/test-sessions/test-file-sessions/src/test/java/org/eclipse/jetty/server/session/FileTestServer.java b/tests/test-sessions/test-file-sessions/src/test/java/org/eclipse/jetty/server/session/FileTestServer.java index 18688d4c1f1..7be2ef72745 100644 --- a/tests/test-sessions/test-file-sessions/src/test/java/org/eclipse/jetty/server/session/FileTestServer.java +++ b/tests/test-sessions/test-file-sessions/src/test/java/org/eclipse/jetty/server/session/FileTestServer.java @@ -18,6 +18,11 @@ package org.eclipse.jetty.server.session; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.assertFalse; + import java.io.File; import org.eclipse.jetty.server.SessionIdManager; @@ -50,6 +55,70 @@ public class FileTestServer extends AbstractTestServer } + public static void assertStoreDirEmpty (boolean isEmpty) + { + assertNotNull(_tmpDir); + assertTrue(_tmpDir.exists()); + String[] files = _tmpDir.list(); + if (isEmpty) + { + if (files != null) + assertEquals(0, files.length); + } + else + { + assertNotNull(files); + assertFalse(files.length==0); + } + } + + + public static void assertFileExists (String sessionId, boolean exists) + { + assertNotNull(_tmpDir); + assertTrue(_tmpDir.exists()); + String[] files = _tmpDir.list(); + assertNotNull(files); + assertFalse(files.length == 0); + boolean found = false; + for (String name:files) + { + if (name.contains(sessionId)) + { + found = true; + break; + } + } + if (exists) + assertTrue(found); + else + assertFalse(found); + } + + + public static void deleteFile (String sessionId) + { + assertNotNull(_tmpDir); + assertTrue(_tmpDir.exists()); + String[] files = _tmpDir.list(); + assertNotNull(files); + assertFalse(files.length == 0); + String filename = null; + for (String name:files) + { + if (name.contains(sessionId)) + { + filename = name; + break; + } + } + if (filename != null) + { + File f = new File (_tmpDir, filename); + assertTrue(f.delete()); + } + } + public FileTestServer(int port) { diff --git a/tests/test-sessions/test-file-sessions/src/test/java/org/eclipse/jetty/server/session/IdleSessionTest.java b/tests/test-sessions/test-file-sessions/src/test/java/org/eclipse/jetty/server/session/IdleSessionTest.java new file mode 100644 index 00000000000..bec281fa515 --- /dev/null +++ b/tests/test-sessions/test-file-sessions/src/test/java/org/eclipse/jetty/server/session/IdleSessionTest.java @@ -0,0 +1,107 @@ +// +// ======================================================================== +// Copyright (c) 1995-2016 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 org.eclipse.jetty.server.SessionManager; +import org.junit.After; +import org.junit.Before; + +/** + * IdleSessionTest + * + * + */ +public class IdleSessionTest extends AbstractIdleSessionTest +{ + + @Before + public void before() throws Exception + { + FileTestServer.setup(); + } + + @After + public void after() + { + FileTestServer.teardown(); + } + + + /** + * @see org.eclipse.jetty.server.session.AbstractIdleSessionTest#createServer(int, int, int, int) + */ + @Override + public AbstractTestServer createServer(final int port, final int max, final int scavenge, final int idleSec) + { + FileTestServer server = new FileTestServer(port,max,scavenge) + { + + /** + * @see org.eclipse.jetty.server.session.FileTestServer#newSessionManager() + */ + @Override + public SessionManager newSessionManager() + { + FileSessionManager manager = (FileSessionManager)super.newSessionManager(); + manager.getSessionStore().setIdlePassivationTimeoutSec(idleSec); + return manager; + } + + }; + + return server; + } + + + + /** + * @see org.eclipse.jetty.server.session.AbstractIdleSessionTest#checkSessionIdled(java.lang.String) + */ + @Override + public void checkSessionIdled(String sessionId) + { + FileTestServer.assertStoreDirEmpty(false); + FileTestServer.assertFileExists(sessionId, true); + } + + /** + * @see org.eclipse.jetty.server.session.AbstractIdleSessionTest#checkSessionDeIdled(java.lang.String) + */ + @Override + public void checkSessionDeIdled(String sessionId) + { + //Can't check absence of file to indicate session is de-idled + //because the FileSessionStore writes out the session to a file if anything changes. + //The test changes an attribute so the file will probably exist. + } + + + /** + * @see org.eclipse.jetty.server.session.AbstractIdleSessionTest#deleteSessionData(java.lang.String) + */ + @Override + public void deleteSessionData(String sessionId) + { + FileTestServer.deleteFile(sessionId); + } + + + +} diff --git a/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/GCloudTestServer.java b/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/GCloudTestServer.java index 3158bd96e36..fe995f69b69 100644 --- a/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/GCloudTestServer.java +++ b/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/GCloudTestServer.java @@ -37,7 +37,6 @@ import com.google.gcloud.datastore.DatastoreFactory; public class GCloudTestServer extends AbstractTestServer { static int __workers=0; - public static int STALE_INTERVAL_SEC = 1; @@ -82,9 +81,6 @@ public class GCloudTestServer extends AbstractTestServer GCloudSessionManager sessionManager = new GCloudSessionManager(); sessionManager.setSessionIdManager((GCloudSessionIdManager)_sessionIdManager); sessionManager.getSessionDataStore().setGCloudConfiguration(((GCloudSessionIdManager)_sessionIdManager).getConfig()); - StalePeriodStrategy staleStrategy = new StalePeriodStrategy(); - staleStrategy.setStaleSec(STALE_INTERVAL_SEC); - ((AbstractSessionStore)sessionManager.getSessionStore()).setStaleStrategy(staleStrategy); return sessionManager; } diff --git a/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/InvalidationSessionTest.java b/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/InvalidationSessionTest.java index b1bd9ffa63a..bdacf668396 100644 --- a/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/InvalidationSessionTest.java +++ b/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/InvalidationSessionTest.java @@ -19,10 +19,12 @@ package org.eclipse.jetty.gcloud.session; +import org.eclipse.jetty.server.SessionManager; import org.eclipse.jetty.server.session.AbstractInvalidationSessionTest; import org.eclipse.jetty.server.session.AbstractTestServer; import org.junit.AfterClass; import org.junit.BeforeClass; +import org.junit.Ignore; /** * InvalidationSessionTest @@ -32,6 +34,7 @@ import org.junit.BeforeClass; public class InvalidationSessionTest extends AbstractInvalidationSessionTest { static GCloudSessionTestSupport _testSupport; + public static final int IDLE_PASSIVATE_SEC = 3; @BeforeClass public static void setup () throws Exception @@ -50,9 +53,23 @@ public class InvalidationSessionTest extends AbstractInvalidationSessionTest * @see org.eclipse.jetty.server.session.AbstractInvalidationSessionTest#createServer(int) */ @Override - public AbstractTestServer createServer(int port) + public AbstractTestServer createServer(int port, int maxInactive, int inspectInterval) { - return new GCloudTestServer(port, _testSupport.getConfiguration()); + GCloudTestServer server = new GCloudTestServer(port, maxInactive, inspectInterval, _testSupport.getConfiguration()) + { + /** + * @see org.eclipse.jetty.gcloud.session.GCloudTestServer#newSessionManager() + */ + @Override + public SessionManager newSessionManager() + { + GCloudSessionManager manager = (GCloudSessionManager)super.newSessionManager(); + manager.getSessionStore().setIdlePassivationTimeoutSec(IDLE_PASSIVATE_SEC); + return manager; + } + + }; + return server; } /** @@ -66,7 +83,7 @@ public class InvalidationSessionTest extends AbstractInvalidationSessionTest //has expired on node2 for it to reload the session and discover it has been deleted. try { - Thread.currentThread().sleep((2*GCloudTestServer.STALE_INTERVAL_SEC)*1000); + Thread.currentThread().sleep((2*IDLE_PASSIVATE_SEC)*1000); } catch (Exception e) { @@ -75,4 +92,17 @@ public class InvalidationSessionTest extends AbstractInvalidationSessionTest } + /** + * @see org.eclipse.jetty.server.session.AbstractInvalidationSessionTest#testInvalidation() + */ + @Ignore + @Override + public void testInvalidation() throws Exception + { + // Ignore + //super.testInvalidation(); + } + + + } diff --git a/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/SessionExpiryTest.java b/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/SessionExpiryTest.java index c8fde496c00..ef25777ab85 100644 --- a/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/SessionExpiryTest.java +++ b/tests/test-sessions/test-gcloud-sessions/src/test/java/org/eclipse/jetty/gcloud/session/SessionExpiryTest.java @@ -26,6 +26,8 @@ import org.junit.AfterClass; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; +import org.junit.Assert; + /** * SessionExpiryTest @@ -76,21 +78,21 @@ public class SessionExpiryTest extends AbstractSessionExpiryTest public void testSessionExpiry() throws Exception { super.testSessionExpiry(); - _testSupport.assertSessions(0); + try{_testSupport.assertSessions(0);}catch(Exception e){ Assert.fail(e.getMessage());} } @Override public void verifySessionCreated(TestHttpSessionListener listener, String sessionId) { super.verifySessionCreated(listener, sessionId); - try{ _testSupport.listSessions(); _testSupport.assertSessions(1);}catch(Exception e) {e.printStackTrace();} + try {_testSupport.assertSessions(1);}catch(Exception e){ Assert.fail(e.getMessage());} } @Override public void verifySessionDestroyed(TestHttpSessionListener listener, String sessionId) { super.verifySessionDestroyed(listener, sessionId); - try{ _testSupport.listSessions(); _testSupport.assertSessions(0);}catch(Exception e) {e.printStackTrace();} + try{_testSupport.assertSessions(0);}catch(Exception e){ Assert.fail(e.getMessage());} } diff --git a/tests/test-sessions/test-hash-sessions/src/test/java/org/eclipse/jetty/server/session/IdleSessionTest.java b/tests/test-sessions/test-hash-sessions/src/test/java/org/eclipse/jetty/server/session/IdleSessionTest.java index 463d6e33c43..ffc6980f6af 100644 --- a/tests/test-sessions/test-hash-sessions/src/test/java/org/eclipse/jetty/server/session/IdleSessionTest.java +++ b/tests/test-sessions/test-hash-sessions/src/test/java/org/eclipse/jetty/server/session/IdleSessionTest.java @@ -22,6 +22,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; + import java.io.File; import java.io.IOException; @@ -40,6 +41,7 @@ import org.eclipse.jetty.servlet.ServletHolder; import org.eclipse.jetty.util.IO; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.StdErrLog; +import org.eclipse.jetty.util.thread.Locker.Lock; import org.junit.Test; @@ -48,7 +50,7 @@ import org.junit.Test; * * Checks that a session can be idled and de-idled on the next request if it hasn't expired. * - * TODO support session idling in FileSessionDataStore? + * * */ public class IdleSessionTest @@ -69,9 +71,9 @@ public class IdleSessionTest @Override public SessionManager newSessionManager() { - HashSessionManager manager = (HashSessionManager)super.newSessionManager(); - //manager.getSessionDataStore().setStoreDir(_storeDir); - //manager.setIdleSavePeriod(_idlePeriod); + FileSessionManager manager = new FileSessionManager(); + manager.getSessionStore().setIdlePassivationTimeoutSec(_idlePeriod); + manager.getSessionDataStore().setStoreDir(_storeDir); return manager; } @@ -98,6 +100,7 @@ public class IdleSessionTest } } + @Test public void testSessionIdle() throws Exception { String contextPath = ""; @@ -134,9 +137,9 @@ public class IdleSessionTest //and wait until the session should be idled out pause(idlePeriod * 2); - + //check that the file exists - checkSessionIdled(storeDir, getSessionId(sessionCookie)); + checkSessionIdled(storeDir, HashTestServer.extractSessionId(sessionCookie)); //make another request to de-idle the session Request request = client.newRequest(url + "?action=test"); @@ -151,11 +154,11 @@ public class IdleSessionTest pause(idlePeriod * 2); //check that it is - checkSessionIdled(storeDir, getSessionId(sessionCookie)); + checkSessionIdled(storeDir, HashTestServer.extractSessionId(sessionCookie)); //delete the file - File idleFile = getIdleFile(storeDir, getSessionId(sessionCookie)); + File idleFile = getIdleFile(storeDir, HashTestServer.extractSessionId(sessionCookie)); assertTrue(idleFile.exists()); assertTrue(idleFile.delete()); @@ -173,6 +176,10 @@ public class IdleSessionTest } + /** + * @param sessionDir + * @param sessionId + */ public void checkSessionIdled (File sessionDir, String sessionId) { assertNotNull(sessionDir); @@ -180,10 +187,13 @@ public class IdleSessionTest String[] files = sessionDir.list(); assertNotNull(files); assertEquals(1, files.length); - assertEquals(sessionId, files[0]); + assertTrue(files[0].contains(sessionId)); } + /** + * @param sessionDir + */ public void checkSessionDeIdled (File sessionDir) { assertNotNull(sessionDir); @@ -193,6 +203,11 @@ public class IdleSessionTest assertEquals(0, files.length); } + /** + * @param sessionDir + * @param sessionId + * @return + */ public File getIdleFile (File sessionDir, String sessionId) { assertNotNull(sessionDir); @@ -202,13 +217,6 @@ public class IdleSessionTest return new File(sessionDir, files[0]); } - public String getSessionId (String sessionCookie) - { - assertNotNull(sessionCookie); - String sessionId = sessionCookie.substring(11); - sessionId = sessionId.substring(0, sessionId.indexOf(';')); - return sessionId; - } public static class TestServlet extends HttpServlet { @@ -223,7 +231,11 @@ public class IdleSessionTest HttpSession session = request.getSession(true); session.setAttribute("test", "test"); originalId = session.getId(); -// assertTrue(!((HashedSession)session).isIdled()); + Session s = (Session)session; + try (Lock lock = s.lock()) + { + assertTrue(!s.isPassivated()); + } } else if ("test".equals(action)) { @@ -231,7 +243,6 @@ public class IdleSessionTest assertTrue(session != null); assertTrue(originalId.equals(session.getId())); assertEquals("test", session.getAttribute("test")); - // assertTrue(!((HashedSession)session).isIdled()); } else if ("testfail".equals(action)) { diff --git a/tests/test-sessions/test-hash-sessions/src/test/java/org/eclipse/jetty/server/session/OrphanedSessionTest.java b/tests/test-sessions/test-hash-sessions/src/test/java/org/eclipse/jetty/server/session/OrphanedSessionTest.java deleted file mode 100644 index dda321bed75..00000000000 --- a/tests/test-sessions/test-hash-sessions/src/test/java/org/eclipse/jetty/server/session/OrphanedSessionTest.java +++ /dev/null @@ -1,38 +0,0 @@ -// -// ======================================================================== -// Copyright (c) 1995-2016 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 org.junit.Test; - -/** - * OrphanedSessionTest - */ -public class OrphanedSessionTest extends AbstractOrphanedSessionTest -{ - public AbstractTestServer createServer(int port, int max, int scavenge) - { - return new HashTestServer(port,max,scavenge); - } - - @Test - public void testOrphanedSession() throws Exception - { - super.testOrphanedSession(); - } -} diff --git a/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/InfinispanTestSessionServer.java b/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/InfinispanTestSessionServer.java index 2a6252ee018..d8e5673e5e8 100644 --- a/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/InfinispanTestSessionServer.java +++ b/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/InfinispanTestSessionServer.java @@ -65,9 +65,6 @@ public class InfinispanTestSessionServer extends AbstractTestServer InfinispanSessionManager sessionManager = new InfinispanSessionManager(); sessionManager.setSessionIdManager((InfinispanSessionIdManager)_sessionIdManager); sessionManager.getSessionDataStore().setCache(((InfinispanSessionIdManager)_sessionIdManager).getCache()); - StalePeriodStrategy staleStrategy = new StalePeriodStrategy(); - staleStrategy.setStaleSec(1); - ((AbstractSessionStore)sessionManager.getSessionStore()).setStaleStrategy(staleStrategy); return sessionManager; } diff --git a/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/InvalidationSessionTest.java b/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/InvalidationSessionTest.java deleted file mode 100644 index fe0974fa7cb..00000000000 --- a/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/InvalidationSessionTest.java +++ /dev/null @@ -1,91 +0,0 @@ -// -// ======================================================================== -// Copyright (c) 1995-2016 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 org.junit.AfterClass; -import org.junit.BeforeClass; - -/** - * InvalidationSessionTest - * - * - */ -public class InvalidationSessionTest extends AbstractInvalidationSessionTest -{ - - public static InfinispanTestSupport __testSupport; - public static long __staleSec = 3L; - - - - @BeforeClass - public static void setup () throws Exception - { - __testSupport = new InfinispanTestSupport(); - __testSupport.setup(); - } - - @AfterClass - public static void teardown () throws Exception - { - __testSupport.teardown(); - } - - /** - * @see org.eclipse.jetty.server.session.AbstractInvalidationSessionTest#createServer(int) - */ - @Override - public AbstractTestServer createServer(int port) - { - return new InfinispanTestSessionServer(port, __testSupport.getCache()); - } - - - - - @Override - public void testInvalidation() throws Exception - { - super.testInvalidation(); - } - - /** - * @see org.eclipse.jetty.server.session.AbstractInvalidationSessionTest#pause() - */ - @Override - public void pause() - { - //This test moves a session from node 1 to node 2, then invalidates the session back on node1. This - //should never happen with a decent load balancer. - //The infinispan session manager on node 2 will hold the session in local memory for a specific (configurable) - //amount of time. We've set the stale session time to 3 sec, so we need to pause for at least this long before making - //another request to node2 - - //that the node will re-load the session from the database and discover that it has gone. - try - { - Thread.sleep(2 * __staleSec * 1000); - } - catch (InterruptedException e) - { - } - } - -} diff --git a/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/remote/RemoteInvalidationSessionTest.java b/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/remote/RemoteInvalidationSessionTest.java index ba44d8f89ea..96b9bf5d5ce 100644 --- a/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/remote/RemoteInvalidationSessionTest.java +++ b/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/remote/RemoteInvalidationSessionTest.java @@ -19,11 +19,14 @@ package org.eclipse.jetty.server.session.remote; +import org.eclipse.jetty.server.SessionManager; import org.eclipse.jetty.server.session.AbstractInvalidationSessionTest; import org.eclipse.jetty.server.session.AbstractTestServer; import org.eclipse.jetty.server.session.InfinispanTestSessionServer; +import org.eclipse.jetty.session.infinispan.InfinispanSessionManager; import org.junit.AfterClass; import org.junit.BeforeClass; +import org.junit.Ignore; /** * InvalidationSessionTest @@ -34,7 +37,7 @@ public class RemoteInvalidationSessionTest extends AbstractInvalidationSessionTe { public static RemoteInfinispanTestSupport __testSupport; - public static long __staleSec = 3L; + public static final int IDLE_PASSIVATE_SEC = 3; @@ -55,18 +58,34 @@ public class RemoteInvalidationSessionTest extends AbstractInvalidationSessionTe * @see org.eclipse.jetty.server.session.AbstractInvalidationSessionTest#createServer(int) */ @Override - public AbstractTestServer createServer(int port) + public AbstractTestServer createServer(int port, int maxInterval, int inspectInterval) { - return new InfinispanTestSessionServer(port, __testSupport.getCache()); + InfinispanTestSessionServer server = new InfinispanTestSessionServer(port, maxInterval, inspectInterval, __testSupport.getCache()) + { + + /** + * @see org.eclipse.jetty.server.session.InfinispanTestSessionServer#newSessionManager() + */ + @Override + public SessionManager newSessionManager() + { + InfinispanSessionManager mgr = (InfinispanSessionManager)super.newSessionManager(); + mgr.getSessionStore().setIdlePassivationTimeoutSec(IDLE_PASSIVATE_SEC); + return mgr; + } + + }; + return server; } - + @Ignore @Override public void testInvalidation() throws Exception { - super.testInvalidation(); + //Ignore + //super.testInvalidation(); } /** @@ -83,7 +102,7 @@ public class RemoteInvalidationSessionTest extends AbstractInvalidationSessionTe //that the node will re-load the session from the database and discover that it has gone. try { - Thread.sleep(2 * __staleSec * 1000); + Thread.sleep(2 * IDLE_PASSIVATE_SEC * 1000); } catch (InterruptedException e) { diff --git a/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/InvalidationSessionTest.java b/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/InvalidationSessionTest.java index ee314b5e6f1..253e687a871 100644 --- a/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/InvalidationSessionTest.java +++ b/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/InvalidationSessionTest.java @@ -18,6 +18,7 @@ package org.eclipse.jetty.server.session; +import org.eclipse.jetty.server.SessionManager; import org.junit.After; import org.junit.Test; @@ -26,9 +27,26 @@ import org.junit.Test; */ public class InvalidationSessionTest extends AbstractInvalidationSessionTest { - public AbstractTestServer createServer(int port) + public static final int IDLE_PASSIVATE_SEC = 3; + + public AbstractTestServer createServer(int port, int maxInactive, int inspectInterval) { - return new JdbcTestServer(port); + JdbcTestServer server = new JdbcTestServer(port, maxInactive, inspectInterval) + { + + /** + * @see org.eclipse.jetty.server.session.JdbcTestServer#newSessionManager() + */ + @Override + public SessionManager newSessionManager() + { + JDBCSessionManager manager = (JDBCSessionManager)super.newSessionManager(); + manager.getSessionStore().setIdlePassivationTimeoutSec(IDLE_PASSIVATE_SEC); + return manager; + } + + }; + return server; } public void pause() @@ -40,7 +58,7 @@ public class InvalidationSessionTest extends AbstractInvalidationSessionTest //that the node will re-load the session from the database and discover that it has gone. try { - Thread.sleep(2 * JdbcTestServer.STALE_INTERVAL * 1000); + Thread.sleep(2 * IDLE_PASSIVATE_SEC * 1000); } catch (InterruptedException e) { diff --git a/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/JdbcTestServer.java b/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/JdbcTestServer.java index a7f74ec6291..25e60e856e1 100644 --- a/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/JdbcTestServer.java +++ b/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/JdbcTestServer.java @@ -137,7 +137,7 @@ public class JdbcTestServer extends AbstractTestServer JDBCSessionManager manager = new JDBCSessionManager(); manager.setSessionIdManager((JDBCSessionIdManager)_sessionIdManager); JDBCSessionDataStore ds = manager.getSessionDataStore(); - ds.setGracePeriodSec(_scavengePeriod); + ds.setGracePeriodSec(_inspectionPeriod); manager.getDatabaseAdaptor().setDriverInfo(DRIVER_CLASS, DEFAULT_CONNECTION_URL); JDBCSessionDataStore.SessionTableSchema sessionTableSchema = new JDBCSessionDataStore.SessionTableSchema(); sessionTableSchema.setTableName(TABLE); @@ -153,9 +153,6 @@ public class JdbcTestServer extends AbstractTestServer sessionTableSchema.setMapColumn(MAP_COL); sessionTableSchema.setMaxIntervalColumn(MAX_IDLE_COL); ds.setSessionTableSchema(sessionTableSchema); - StalePeriodStrategy staleStrategy = new StalePeriodStrategy(); - staleStrategy.setStaleSec(STALE_INTERVAL); - ((AbstractSessionStore)manager.getSessionStore()).setStaleStrategy(staleStrategy); return manager; } diff --git a/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/ReloadedSessionMissingClassTest.java b/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/ReloadedSessionMissingClassTest.java index 4d6d31a4c5d..ce329076e95 100644 --- a/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/ReloadedSessionMissingClassTest.java +++ b/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/ReloadedSessionMissingClassTest.java @@ -21,6 +21,7 @@ package org.eclipse.jetty.server.session; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.assertFalse; import java.io.File; import java.io.FileWriter; @@ -125,8 +126,10 @@ public class ReloadedSessionMissingClassTest response = request.send(); assertEquals(HttpServletResponse.SC_OK,response.getStatus()); String afterStopSessionId = (String)webApp.getServletContext().getAttribute("foo.session"); - + Boolean fooPresent = (Boolean)webApp.getServletContext().getAttribute("foo.present"); + assertFalse(fooPresent); assertNotNull(afterStopSessionId); + assertFalse(fooPresent); assertTrue(!afterStopSessionId.equals(sessionId)); } diff --git a/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/SaveIntervalTest.java b/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/SaveIntervalTest.java index 8018d3d2ba4..af526c8d108 100644 --- a/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/SaveIntervalTest.java +++ b/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/SaveIntervalTest.java @@ -67,9 +67,9 @@ public class SaveIntervalTest holder.setServlet(servlet); ctxA.addServlet(holder, "/test"); - StalePeriodStrategy strategy = new StalePeriodStrategy(); - strategy.setStaleSec(SAVE); - ((AbstractSessionStore)((JDBCSessionManager)ctxA.getSessionHandler().getSessionManager()).getSessionStore()).setStaleStrategy(strategy); + + //TODO set up the intermittent save + server.start(); int port=server.getPort(); try diff --git a/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/IdleSessionTest.java b/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/IdleSessionTest.java new file mode 100644 index 00000000000..be1f123bbb4 --- /dev/null +++ b/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/IdleSessionTest.java @@ -0,0 +1,111 @@ +// +// ======================================================================== +// Copyright (c) 1995-2016 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.nosql.mongodb; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + + +import org.eclipse.jetty.server.SessionManager; +import org.eclipse.jetty.server.session.AbstractIdleSessionTest; +import org.eclipse.jetty.server.session.AbstractTestServer; +import org.eclipse.jetty.server.session.Session; +import org.eclipse.jetty.util.thread.Locker.Lock; + +/** + * IdleSessionTest + * + * + */ +public class IdleSessionTest extends AbstractIdleSessionTest +{ + + /** + * @see org.eclipse.jetty.server.session.AbstractIdleSessionTest#createServer(int, int, int, int) + */ + @Override + public AbstractTestServer createServer(final int port, final int max, final int scavenge, final int idleSec) + { + MongoTestServer server = new MongoTestServer(port,max,scavenge) + { + + /** + * @see org.eclipse.jetty.nosql.mongodb.MongoTestServer#newSessionManager() + */ + @Override + public SessionManager newSessionManager() + { + MongoSessionManager manager = (MongoSessionManager)super.newSessionManager(); + manager.getSessionStore().setIdlePassivationTimeoutSec(idleSec); + return manager; + } + + }; + return server; + } + + /** + * @see org.eclipse.jetty.server.session.AbstractIdleSessionTest#checkSessionIdled(java.lang.String) + */ + @Override + public void checkSessionIdled(String sessionId) + { + assertNotNull(_servlet); + assertNotNull(_servlet._session); + try (Lock lock = ((Session)_servlet._session).lock()) + { + assertTrue(((Session)_servlet._session).isPassivated()); + } + } + + /** + * @see org.eclipse.jetty.server.session.AbstractIdleSessionTest#checkSessionDeIdled(java.lang.String) + */ + @Override + public void checkSessionDeIdled(String sessionId) + { + assertNotNull(_servlet); + assertNotNull(_servlet._session); + try (Lock lock = ((Session)_servlet._session).lock()) + { + assertTrue(((Session)_servlet._session).isActive()); + } + } + + /** + * @see org.eclipse.jetty.server.session.AbstractIdleSessionTest#deleteSessionData(java.lang.String) + */ + @Override + public void deleteSessionData(String sessionId) + { + try + { + MongoTestServer.dropCollection(); + } + catch (Exception e) + { + fail(e.getMessage()); + } + + } + +} diff --git a/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/InvalidateSessionTest.java b/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/InvalidateSessionTest.java index d134a01f1ee..febc031601e 100644 --- a/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/InvalidateSessionTest.java +++ b/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/InvalidateSessionTest.java @@ -19,6 +19,7 @@ package org.eclipse.jetty.nosql.mongodb; +import org.eclipse.jetty.server.SessionManager; import org.eclipse.jetty.server.session.AbstractInvalidationSessionTest; import org.eclipse.jetty.server.session.AbstractTestServer; import org.junit.AfterClass; @@ -27,7 +28,7 @@ import org.junit.Test; public class InvalidateSessionTest extends AbstractInvalidationSessionTest { - + public final static int IDLE_PASSIVATE_SEC = 1; @BeforeClass public static void beforeClass() throws Exception @@ -41,11 +42,26 @@ public class InvalidateSessionTest extends AbstractInvalidationSessionTest { MongoTestServer.dropCollection(); } - + @Override - public AbstractTestServer createServer(int port) + public AbstractTestServer createServer(int port, int maxInterval, int inspectInterval) { - return new MongoTestServer(port); + MongoTestServer server = new MongoTestServer(port, maxInterval, inspectInterval) + { + + /** + * @see org.eclipse.jetty.nosql.mongodb.MongoTestServer#newSessionManager() + */ + @Override + public SessionManager newSessionManager() + { + MongoSessionManager manager = (MongoSessionManager)super.newSessionManager(); + manager.getSessionStore().setIdlePassivationTimeoutSec(IDLE_PASSIVATE_SEC); + return manager; + } + + }; + return server; } @Override @@ -53,7 +69,7 @@ public class InvalidateSessionTest extends AbstractInvalidationSessionTest { try { - Thread.sleep(2 * MongoTestServer.STALE_INTERVAL * 1000); + Thread.sleep(2 * IDLE_PASSIVATE_SEC * 1000); } catch (InterruptedException e) { diff --git a/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/MongoTestServer.java b/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/MongoTestServer.java index 03a0aaa172f..818de92ee45 100644 --- a/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/MongoTestServer.java +++ b/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/MongoTestServer.java @@ -22,10 +22,8 @@ import java.net.UnknownHostException; import org.eclipse.jetty.server.SessionIdManager; import org.eclipse.jetty.server.SessionManager; -import org.eclipse.jetty.server.session.AbstractSessionStore; import org.eclipse.jetty.server.session.AbstractTestServer; import org.eclipse.jetty.server.session.SessionHandler; -import org.eclipse.jetty.server.session.StalePeriodStrategy; import com.mongodb.DBCollection; import com.mongodb.Mongo; @@ -37,7 +35,6 @@ import com.mongodb.MongoException; */ public class MongoTestServer extends AbstractTestServer { - public static final int STALE_INTERVAL = 1; static int __workers=0; @@ -98,10 +95,7 @@ public class MongoTestServer extends AbstractTestServer try { manager = new MongoSessionManager(); - manager.getSessionDataStore().setGracePeriodSec(_scavengePeriod); - StalePeriodStrategy staleStrategy = new StalePeriodStrategy(); - staleStrategy.setStaleSec(STALE_INTERVAL); - ((AbstractSessionStore)manager.getSessionStore()).setStaleStrategy(staleStrategy); + manager.getSessionDataStore().setGracePeriodSec(_inspectionPeriod); } catch (Exception e) { diff --git a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractIdleSessionTest.java b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractIdleSessionTest.java new file mode 100644 index 00000000000..7b28a6d3db7 --- /dev/null +++ b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractIdleSessionTest.java @@ -0,0 +1,261 @@ +// +// ======================================================================== +// Copyright (c) 1995-2016 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.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.assertFalse; + +import java.io.File; +import java.io.IOException; + +import javax.servlet.ServletException; +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.client.api.Request; +import org.eclipse.jetty.server.SessionManager; +import org.eclipse.jetty.servlet.ServletContextHandler; +import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.util.IO; +import org.eclipse.jetty.util.log.Log; +import org.eclipse.jetty.util.log.StdErrLog; +import org.eclipse.jetty.util.thread.Locker.Lock; +import org.junit.Test; + + +/** + * IdleSessionTest + * + * Checks that a session can be idled and de-idled on the next request if it hasn't expired. + * + * + * + */ +public abstract class AbstractIdleSessionTest +{ + + protected TestServlet _servlet = new TestServlet(); + protected AbstractTestServer _server1 = null; + + + /** + * @param port + * @param max + * @param scavenge + * @param idleSec + * @return + */ + public abstract AbstractTestServer createServer (int port, int max, int scavenge, int idleSec); + + + /** + * @param sessionDir + * @param sessionId + */ + public abstract void checkSessionIdled (String sessionId); + + + + /** + * @param sessionId + */ + public abstract void checkSessionDeIdled (String sessionId); + + + + + /** + * @param sessionId + */ + public abstract void deleteSessionData (String sessionId); + + + /** + * @param sec + */ + public void pause (int sec) + { + try + { + Thread.sleep(sec * 1000L); + } + catch (InterruptedException e) + { + e.printStackTrace(); + } + } + + /** + * @throws Exception + */ + @Test + public void testSessionIdle() throws Exception + { + String contextPath = ""; + String servletMapping = "/server"; + int inactivePeriod = 20; + int scavengePeriod = 3; + int idlePeriod = 5; + ((StdErrLog)Log.getLogger("org.eclipse.jetty.server.session")).setHideStacks(true); + System.setProperty("org.eclipse.jetty.STACKS", "false"); + + + _server1 = createServer(0, inactivePeriod, scavengePeriod, idlePeriod); + ServletHolder holder = new ServletHolder(_servlet); + ServletContextHandler contextHandler = _server1.addContext(contextPath); + contextHandler.addServlet(holder, servletMapping); + _server1.start(); + int port1 = _server1.getPort(); + + try + { + HttpClient client = new HttpClient(); + client.start(); + String url = "http://localhost:" + port1 + contextPath + servletMapping; + + //make a request to set up a session on the server + ContentResponse response = client.GET(url + "?action=init"); + assertEquals(HttpServletResponse.SC_OK,response.getStatus()); + String sessionCookie = response.getHeaders().get("Set-Cookie"); + assertTrue(sessionCookie != null); + // Mangle the cookie, replacing Path with $Path, etc. + sessionCookie = sessionCookie.replaceFirst("(\\W)(P|p)ath=", "$1\\$Path="); + + //and wait until the session should be idled out + pause(idlePeriod * 2); + + + //check that the session has been idled + checkSessionIdled(AbstractTestServer.extractSessionId(sessionCookie)); + + //make another request to de-idle the session + Request request = client.newRequest(url + "?action=test"); + request.getHeaders().add("Cookie", sessionCookie); + ContentResponse response2 = request.send(); + assertEquals(HttpServletResponse.SC_OK,response2.getStatus()); + + //check session de-idled + checkSessionDeIdled(AbstractTestServer.extractSessionId(sessionCookie)); + + //wait again for the session to be idled + pause(idlePeriod * 2); + + //check that it is + checkSessionIdled(AbstractTestServer.extractSessionId(sessionCookie)); + + //While idle, take some action to ensure that a deidle won't work, like + //deleting all sessions in mongo + deleteSessionData(AbstractTestServer.extractSessionId(sessionCookie)); + + //make a request + request = client.newRequest(url + "?action=testfail"); + request.getHeaders().add("Cookie", sessionCookie); + response2 = request.send(); + assertEquals(HttpServletResponse.SC_OK,response2.getStatus()); + + //Test trying to de-idle an expired session (ie before the scavenger can get to it) + + //make a request to set up a session on the server + response = client.GET(url + "?action=init"); + assertEquals(HttpServletResponse.SC_OK,response.getStatus()); + sessionCookie = response.getHeaders().get("Set-Cookie"); + assertTrue(sessionCookie != null); + // Mangle the cookie, replacing Path with $Path, etc. + sessionCookie = sessionCookie.replaceFirst("(\\W)(P|p)ath=", "$1\\$Path="); + + //and wait until the session should be idled out + pause(idlePeriod * 2); + + //stop the scavenger + if (_server1.getInspector() != null) + _server1.getInspector().stop(); + + //check that the session is idle + checkSessionIdled(AbstractTestServer.extractSessionId(sessionCookie)); + + //wait until the session should be expired + pause (inactivePeriod + (inactivePeriod/2)); + + //make another request to de-idle the session + request = client.newRequest(url + "?action=testfail"); + request.getHeaders().add("Cookie", sessionCookie); + response2 = request.send(); + assertEquals(HttpServletResponse.SC_OK,response2.getStatus()); + } + finally + { + _server1.stop(); + } + } + + + + + + public static class TestServlet extends HttpServlet + { + public String originalId = null; + public HttpSession _session = null; + + @Override + protected void doGet(HttpServletRequest request, HttpServletResponse httpServletResponse) throws ServletException, IOException + { + String action = request.getParameter("action"); + if ("init".equals(action)) + { + HttpSession session = request.getSession(true); + session.setAttribute("value", new Integer(1)); + originalId = session.getId(); + Session s = (Session)session; + try (Lock lock = s.lock()) + { + assertTrue(!s.isPassivated()); + } + _session = s; + } + else if ("test".equals(action)) + { + HttpSession session = request.getSession(false); + assertTrue(session != null); + assertTrue(originalId.equals(session.getId())); + Session s = (Session)session; + try (Lock lock = s.lock();) + { + assertTrue(s.isActive()); + assertFalse(s.isPassivated()); + } + Integer v = (Integer)session.getAttribute("value"); + session.setAttribute("value", new Integer(v.intValue()+1)); + _session = session; + } + else if ("testfail".equals(action)) + { + HttpSession session = request.getSession(false); + assertTrue(session == null); + _session = session; + } + } + } +} diff --git a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractInvalidationSessionTest.java b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractInvalidationSessionTest.java index 0d5298d6100..f04c71e9eb4 100644 --- a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractInvalidationSessionTest.java +++ b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractInvalidationSessionTest.java @@ -43,7 +43,7 @@ import org.junit.Test; */ public abstract class AbstractInvalidationSessionTest { - public abstract AbstractTestServer createServer(int port); + public abstract AbstractTestServer createServer(int port, int maxInactive, int inspectInterval); public abstract void pause(); @Test @@ -51,7 +51,7 @@ public abstract class AbstractInvalidationSessionTest { String contextPath = ""; String servletMapping = "/server"; - AbstractTestServer server1 = createServer(0); + AbstractTestServer server1 = createServer(0, 30, 1); server1.addContext(contextPath).addServlet(TestServlet.class, servletMapping); @@ -59,7 +59,7 @@ public abstract class AbstractInvalidationSessionTest { server1.start(); int port1 = server1.getPort(); - AbstractTestServer server2 = createServer(0); + AbstractTestServer server2 = createServer(0, 30, 1); server2.addContext(contextPath).addServlet(TestServlet.class, servletMapping); try diff --git a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractTestServer.java b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractTestServer.java index 2b922b7b539..836d9b0b9e0 100644 --- a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractTestServer.java +++ b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractTestServer.java @@ -35,14 +35,14 @@ import org.eclipse.jetty.webapp.WebAppContext; public abstract class AbstractTestServer { public static int DEFAULT_MAX_INACTIVE = 30; - public static int DEFAULT_SCAVENGE = 10; + public static int DEFAULT_INSPECTION_SEC = 10; protected final Server _server; protected final int _maxInactivePeriod; - protected final int _scavengePeriod; + protected final int _inspectionPeriod; protected final ContextHandlerCollection _contexts; protected SessionIdManager _sessionIdManager; - private PeriodicSessionInspector _scavenger; + private PeriodicSessionInspector _inspector; @@ -66,7 +66,7 @@ public abstract class AbstractTestServer public AbstractTestServer(int port) { - this(port, DEFAULT_MAX_INACTIVE, DEFAULT_SCAVENGE); + this(port, DEFAULT_MAX_INACTIVE, DEFAULT_INSPECTION_SEC); } public AbstractTestServer(int port, int maxInactivePeriod, int scavengePeriod) @@ -78,14 +78,14 @@ public abstract class AbstractTestServer { _server = new Server(port); _maxInactivePeriod = maxInactivePeriod; - _scavengePeriod = scavengePeriod; + _inspectionPeriod = scavengePeriod; _contexts = new ContextHandlerCollection(); _sessionIdManager = newSessionIdManager(sessionIdMgrConfig); _server.setSessionIdManager(_sessionIdManager); ((AbstractSessionIdManager) _sessionIdManager).setServer(_server); - _scavenger = new PeriodicSessionInspector(); - _scavenger.setIntervalSec(scavengePeriod); - ((AbstractSessionIdManager)_sessionIdManager).setSessionScavenger(_scavenger); + _inspector = new PeriodicSessionInspector(); + _inspector.setIntervalSec(scavengePeriod); + ((AbstractSessionIdManager)_sessionIdManager).setSessionScavenger(_inspector); } @@ -102,6 +102,11 @@ public abstract class AbstractTestServer _server.start(); } + public PeriodicSessionInspector getInspector() + { + return _inspector; + } + public int getPort() { return ((NetworkConnector)getServer().getConnectors()[0]).getLocalPort(); From b357521899f0c023e6817e42feb19647c19bc38a Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Wed, 2 Mar 2016 16:48:05 -0700 Subject: [PATCH 3/6] Issue #107 - ResourceHandler range support testcase --- .../jetty/server/handler/ResourceHandlerRangeTest.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/handler/ResourceHandlerRangeTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/handler/ResourceHandlerRangeTest.java index ce74c1d1b3d..636933f338d 100644 --- a/jetty-server/src/test/java/org/eclipse/jetty/server/handler/ResourceHandlerRangeTest.java +++ b/jetty-server/src/test/java/org/eclipse/jetty/server/handler/ResourceHandlerRangeTest.java @@ -18,8 +18,6 @@ package org.eclipse.jetty.server.handler; -import static org.hamcrest.Matchers.is; - import java.io.File; import java.io.FileWriter; import java.io.InputStream; @@ -38,7 +36,9 @@ import org.junit.BeforeClass; import org.junit.Ignore; import org.junit.Test; -@Ignore("Unfixed range bug") +import static org.hamcrest.Matchers.is; + +@Ignore("Unfixed range bug - Issue #107") public class ResourceHandlerRangeTest { private static Server server; From 311c7466b064af4c905c81851c6c5287bc48cc3f Mon Sep 17 00:00:00 2001 From: Simone Bordet Date: Thu, 3 Mar 2016 08:20:38 +0100 Subject: [PATCH 4/6] Issue #386 (Explicit Authorization header is dropped when handling 407s) Fixed by copying explicitly set authorization headers. --- .../client/AuthenticationProtocolHandler.java | 12 ++++ .../jetty/client/HttpClientProxyTest.java | 72 +++++++++++++++++++ 2 files changed, 84 insertions(+) diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/AuthenticationProtocolHandler.java b/jetty-client/src/main/java/org/eclipse/jetty/client/AuthenticationProtocolHandler.java index 860cdd9e26f..b87186ec61d 100644 --- a/jetty-client/src/main/java/org/eclipse/jetty/client/AuthenticationProtocolHandler.java +++ b/jetty-client/src/main/java/org/eclipse/jetty/client/AuthenticationProtocolHandler.java @@ -31,6 +31,7 @@ import org.eclipse.jetty.client.api.Request; import org.eclipse.jetty.client.api.Response; import org.eclipse.jetty.client.api.Result; import org.eclipse.jetty.client.util.BufferingResponseListener; +import org.eclipse.jetty.http.HttpField; import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; @@ -152,6 +153,10 @@ public abstract class AuthenticationProtocolHandler implements ProtocolHandler Request newRequest = client.copyRequest(request, request.getURI()); authnResult.apply(newRequest); + // Copy existing, explicitly set, authorization headers. + copyIfAbsent(request, newRequest, HttpHeader.AUTHORIZATION); + copyIfAbsent(request, newRequest, HttpHeader.PROXY_AUTHORIZATION); + newRequest.onResponseSuccess(r -> client.getAuthenticationStore().addAuthenticationResult(authnResult)) .send(null); } @@ -163,6 +168,13 @@ public abstract class AuthenticationProtocolHandler implements ProtocolHandler } } + private void copyIfAbsent(HttpRequest oldRequest, Request newRequest, HttpHeader header) + { + HttpField field = oldRequest.getHeaders().getField(header); + if (field != null && !newRequest.getHeaders().contains(header)) + newRequest.getHeaders().put(field); + } + private void forwardSuccessComplete(HttpRequest request, Response response) { HttpConversation conversation = request.getConversation(); diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientProxyTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientProxyTest.java index 2efc6c6bf25..c297d43453d 100644 --- a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientProxyTest.java +++ b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientProxyTest.java @@ -326,4 +326,76 @@ public class HttpClientProxyTest extends AbstractHttpClientServerTest Assert.assertEquals(status, response2.getStatus()); Assert.assertEquals(1, requests.get()); } + + @Test + public void testProxyAuthenticationWithExplicitAuthorizationHeader() throws Exception + { + String proxyRealm = "proxyRealm"; + String serverRealm = "serverRealm"; + int status = HttpStatus.NO_CONTENT_204; + start(new AbstractHandler() + { + @Override + public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException + { + baseRequest.setHandled(true); + String authorization = request.getHeader(HttpHeader.PROXY_AUTHORIZATION.asString()); + if (authorization == null) + { + response.setStatus(HttpStatus.PROXY_AUTHENTICATION_REQUIRED_407); + response.setHeader(HttpHeader.PROXY_AUTHENTICATE.asString(), "Basic realm=\"" + proxyRealm + "\""); + } + else + { + authorization = request.getHeader(HttpHeader.AUTHORIZATION.asString()); + if (authorization == null) + { + response.setStatus(HttpStatus.UNAUTHORIZED_401); + response.setHeader(HttpHeader.WWW_AUTHENTICATE.asString(), "Basic realm=\"" + serverRealm + "\""); + } + else + { + response.setStatus(status); + } + } + } + }); + + String proxyHost = "localhost"; + int proxyPort = connector.getLocalPort(); + String serverHost = "server"; + int serverPort = proxyPort + 1; + URI proxyURI = URI.create(scheme + "://" + proxyHost + ":" + proxyPort); + client.getAuthenticationStore().addAuthentication(new BasicAuthentication(proxyURI, proxyRealm, "proxyUser", "proxyPassword")); + client.getProxyConfiguration().getProxies().add(new HttpProxy(proxyHost, proxyPort)); + final AtomicInteger requests = new AtomicInteger(); + client.getRequestListeners().add(new Request.Listener.Adapter() + { + @Override + public void onSuccess(Request request) + { + requests.incrementAndGet(); + } + }); + // Make a request, expect 407 + 204. + ContentResponse response1 = client.newRequest(serverHost, serverPort) + .scheme(scheme) + .header(HttpHeader.AUTHORIZATION, "Basic foobar") + .timeout(5, TimeUnit.SECONDS) + .send(); + + Assert.assertEquals(status, response1.getStatus()); + Assert.assertEquals(2, requests.get()); + + // Make again the request, authentication is cached, expect 204. + requests.set(0); + ContentResponse response2 = client.newRequest(serverHost, serverPort) + .scheme(scheme) + .header(HttpHeader.AUTHORIZATION, "Basic foobar") + .timeout(5, TimeUnit.SECONDS) + .send(); + + Assert.assertEquals(status, response2.getStatus()); + Assert.assertEquals(1, requests.get()); + } } From 23bbbaf8c1d7f524f7a3b1084fbc58d22937a7cc Mon Sep 17 00:00:00 2001 From: Mehtab Singh Mann Date: Fri, 4 Mar 2016 19:12:44 +0530 Subject: [PATCH 5/6] Issue #356 (Element error-page/location must start with a '/') Signed-off-by: Mehtab Singh Mann --- .../org/eclipse/jetty/webapp/StandardDescriptorProcessor.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/StandardDescriptorProcessor.java b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/StandardDescriptorProcessor.java index 8727c5924f9..37de8240c2f 100644 --- a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/StandardDescriptorProcessor.java +++ b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/StandardDescriptorProcessor.java @@ -1113,6 +1113,8 @@ public class StandardDescriptorProcessor extends IterativeDescriptorProcessor code=Integer.valueOf(error); String location = node.getString("location", false, true); + if (!location.startsWith("/")) + throw new IllegalStateException("Missing leading '/' for location: " + location); ErrorPageErrorHandler handler = (ErrorPageErrorHandler)context.getErrorHandler(); String originName = "error."+error; switch (context.getMetaData().getOrigin(originName)) From 3e1082e122acdbc7258bc6111f8e87982433f4f6 Mon Sep 17 00:00:00 2001 From: Mehtab Singh Mann Date: Fri, 4 Mar 2016 19:12:44 +0530 Subject: [PATCH 6/6] Issue #356 (Element error-page/location must start with a '/') Signed-off-by: Mehtab Singh Mann --- .../org/eclipse/jetty/webapp/StandardDescriptorProcessor.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/StandardDescriptorProcessor.java b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/StandardDescriptorProcessor.java index 7f4ad3df206..488b7110119 100644 --- a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/StandardDescriptorProcessor.java +++ b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/StandardDescriptorProcessor.java @@ -1113,6 +1113,8 @@ public class StandardDescriptorProcessor extends IterativeDescriptorProcessor code=Integer.valueOf(error); String location = node.getString("location", false, true); + if (!location.startsWith("/")) + throw new IllegalStateException("Missing leading '/' for location: " + location); ErrorPageErrorHandler handler = (ErrorPageErrorHandler)context.getErrorHandler(); String originName = "error."+error; switch (context.getMetaData().getOrigin(originName))