Merge branch 'master' into jetty-9.4.x-Feature

This commit is contained in:
Greg Wilkins 2016-03-04 16:05:23 +01:00
commit c5f0c80cd2
36 changed files with 1240 additions and 391 deletions

View File

@ -31,6 +31,7 @@ import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.client.api.Response; import org.eclipse.jetty.client.api.Response;
import org.eclipse.jetty.client.api.Result; import org.eclipse.jetty.client.api.Result;
import org.eclipse.jetty.client.util.BufferingResponseListener; import org.eclipse.jetty.client.util.BufferingResponseListener;
import org.eclipse.jetty.http.HttpField;
import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.util.log.Logger;
@ -40,7 +41,6 @@ public abstract class AuthenticationProtocolHandler implements ProtocolHandler
public static final int DEFAULT_MAX_CONTENT_LENGTH = 16*1024; public static final int DEFAULT_MAX_CONTENT_LENGTH = 16*1024;
public static final Logger LOG = Log.getLogger(AuthenticationProtocolHandler.class); 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 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 HttpClient client;
private final int maxContentLength; private final int maxContentLength;
@ -64,6 +64,8 @@ public abstract class AuthenticationProtocolHandler implements ProtocolHandler
protected abstract URI getAuthenticationURI(Request request); protected abstract URI getAuthenticationURI(Request request);
protected abstract String getAuthenticationAttribute();
@Override @Override
public Response.Listener getResponseListener() public Response.Listener getResponseListener()
{ {
@ -92,8 +94,9 @@ public abstract class AuthenticationProtocolHandler implements ProtocolHandler
return; return;
} }
String authenticationAttribute = getAuthenticationAttribute();
HttpConversation conversation = request.getConversation(); 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 // We have already tried to authenticate, but we failed again
if (LOG.isDebugEnabled()) if (LOG.isDebugEnabled())
@ -146,18 +149,16 @@ public abstract class AuthenticationProtocolHandler implements ProtocolHandler
return; return;
} }
conversation.setAttribute(AUTHENTICATION_ATTRIBUTE, true); conversation.setAttribute(authenticationAttribute, true);
Request newRequest = client.copyRequest(request, request.getURI()); Request newRequest = client.copyRequest(request, request.getURI());
authnResult.apply(newRequest); authnResult.apply(newRequest);
newRequest.onResponseSuccess(new Response.SuccessListener() // Copy existing, explicitly set, authorization headers.
{ copyIfAbsent(request, newRequest, HttpHeader.AUTHORIZATION);
@Override copyIfAbsent(request, newRequest, HttpHeader.PROXY_AUTHORIZATION);
public void onSuccess(Response response)
{ newRequest.onResponseSuccess(r -> client.getAuthenticationStore().addAuthenticationResult(authnResult))
client.getAuthenticationStore().addAuthenticationResult(authnResult); .send(null);
}
}).send(null);
} }
catch (Throwable x) catch (Throwable x)
{ {
@ -167,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) private void forwardSuccessComplete(HttpRequest request, Response response)
{ {
HttpConversation conversation = request.getConversation(); HttpConversation conversation = request.getConversation();

View File

@ -153,14 +153,9 @@ public abstract class HttpConnection implements Connection
request.header(HttpHeader.COOKIE.asString(), cookies.toString()); request.header(HttpHeader.COOKIE.asString(), cookies.toString());
} }
// Authorization // Authentication
URI authenticationURI = proxy != null ? proxy.getURI() : request.getURI(); applyAuthentication(request, proxy != null ? proxy.getURI() : null);
if (authenticationURI != null) applyAuthentication(request, request.getURI());
{
Authentication.Result authnResult = getHttpClient().getAuthenticationStore().findAuthenticationResult(authenticationURI);
if (authnResult != null)
authnResult.apply(request);
}
} }
private StringBuilder convertCookies(List<HttpCookie> cookies, StringBuilder builder) private StringBuilder convertCookies(List<HttpCookie> cookies, StringBuilder builder)
@ -177,6 +172,16 @@ public abstract class HttpConnection implements Connection
return builder; 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) protected SendFailure send(HttpChannel channel, HttpExchange exchange)
{ {
// Forbid idle timeouts for the time window where // Forbid idle timeouts for the time window where

View File

@ -34,6 +34,7 @@ import org.eclipse.jetty.http.HttpStatus;
public class ProxyAuthenticationProtocolHandler extends AuthenticationProtocolHandler public class ProxyAuthenticationProtocolHandler extends AuthenticationProtocolHandler
{ {
public static final String NAME = "proxy-authenticate"; public static final String NAME = "proxy-authenticate";
private static final String ATTRIBUTE = ProxyAuthenticationProtocolHandler.class.getName() + ".attribute";
public ProxyAuthenticationProtocolHandler(HttpClient client) public ProxyAuthenticationProtocolHandler(HttpClient client)
{ {
@ -76,4 +77,10 @@ public class ProxyAuthenticationProtocolHandler extends AuthenticationProtocolHa
ProxyConfiguration.Proxy proxy = destination.getProxy(); ProxyConfiguration.Proxy proxy = destination.getProxy();
return proxy != null ? proxy.getURI() : request.getURI(); return proxy != null ? proxy.getURI() : request.getURI();
} }
@Override
protected String getAuthenticationAttribute()
{
return ATTRIBUTE;
}
} }

View File

@ -34,6 +34,7 @@ import org.eclipse.jetty.http.HttpStatus;
public class WWWAuthenticationProtocolHandler extends AuthenticationProtocolHandler public class WWWAuthenticationProtocolHandler extends AuthenticationProtocolHandler
{ {
public static final String NAME = "www-authenticate"; public static final String NAME = "www-authenticate";
private static final String ATTRIBUTE = WWWAuthenticationProtocolHandler.class.getName() + ".attribute";
public WWWAuthenticationProtocolHandler(HttpClient client) public WWWAuthenticationProtocolHandler(HttpClient client)
{ {
@ -74,4 +75,10 @@ public class WWWAuthenticationProtocolHandler extends AuthenticationProtocolHand
{ {
return request.getURI(); return request.getURI();
} }
@Override
protected String getAuthenticationAttribute()
{
return ATTRIBUTE;
}
} }

View File

@ -81,7 +81,7 @@ public class HttpClientProxyTest extends AbstractHttpClientServerTest
} }
@Test @Test
public void testAuthenticatedProxiedRequest() throws Exception public void testProxyAuthentication() throws Exception
{ {
final String user = "foo"; final String user = "foo";
final String password = "bar"; final String password = "bar";
@ -160,7 +160,7 @@ public class HttpClientProxyTest extends AbstractHttpClientServerTest
} }
@Test @Test
public void testAuthenticatedProxiedRequestWithRedirect() throws Exception public void testProxyAuthenticationWithRedirect() throws Exception
{ {
String user = "foo"; String user = "foo";
String password = "bar"; String password = "bar";
@ -254,4 +254,148 @@ public class HttpClientProxyTest extends AbstractHttpClientServerTest
Assert.assertEquals(status, response3.getStatus()); Assert.assertEquals(status, response3.getStatus());
Assert.assertEquals(1, requests.get()); 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());
}
@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());
}
} }

View File

@ -208,7 +208,7 @@ public class InfinispanSessionDataStore extends AbstractSessionDataStore
try try
{ {
Class<?> remoteClass = Thread.currentThread().getContextClassLoader().loadClass("org.infinispan.client.hotrod.RemoteCache"); Class<?> remoteClass = Thread.currentThread().getContextClassLoader().loadClass("org.infinispan.client.hotrod.RemoteCache");
if (_cache.getClass().isAssignableFrom(remoteClass)) if (remoteClass.isAssignableFrom(_cache.getClass()))
{ {
return true; return true;
} }

View File

@ -44,7 +44,6 @@ public abstract class AbstractSessionStore extends AbstractLifeCycle implements
final static Logger LOG = Log.getLogger("org.eclipse.jetty.server.session"); final static Logger LOG = Log.getLogger("org.eclipse.jetty.server.session");
protected SessionDataStore _sessionDataStore; protected SessionDataStore _sessionDataStore;
protected StalenessStrategy _staleStrategy;
protected SessionManager _manager; protected SessionManager _manager;
protected SessionContext _context; protected SessionContext _context;
protected int _idlePassivationTimeoutSec; protected int _idlePassivationTimeoutSec;
@ -80,6 +79,16 @@ public abstract class AbstractSessionStore extends AbstractLifeCycle implements
public abstract Session doPutIfAbsent (String id, Session session); 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 * 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,21 +212,6 @@ public abstract class AbstractSessionStore extends AbstractLifeCycle implements
_sessionDataStore = sessionDataStore; _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;
}
/** /**
@ -238,53 +250,132 @@ public abstract class AbstractSessionStore extends AbstractLifeCycle implements
@Override @Override
public Session get(String id, boolean staleCheck) throws Exception public Session get(String id, boolean staleCheck) throws Exception
{ {
//look locally Session session = null;
Session session = doGet(id); Exception ex = null;
//TODO also check that session is only written out if only the access time changes infrequently while (true)
//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)
{ {
SessionData data = _sessionDataStore.load(id); session = doGet(id);
if (_sessionDataStore == null)
break; //can't load any session data so just return null or the session object
//session wasn't in session store
if (session == null) 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); //My placeholder won, go ahead and load the full session data
session.setSessionManager(_manager); try
Session existing = doPutIfAbsent(id, session);
if (existing != null)
{ {
//some other thread has got in first and added the session session = loadSession(id);
//so use it if (session == null)
session = existing;
}
}
//else session not in store and not in data store either, so doesn't exist
}
else
{ {
//session was already in session store, refresh it if its still stale/passivated //session does not exist, remove the placeholder
doDelete(id);
phsLock.close();
break;
}
try (Lock lock = session.lock()) try (Lock lock = session.lock())
{ {
if (session.isPassivated() || staleCheck && isStale(session)) //swap it in instead of the placeholder
boolean success = doReplace(id, phs, session);
if (!success)
{ {
//if we were able to load it, then update our session object //something has gone wrong, it should have been our placeholder
if (data != null) 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)
{ {
session.setPassivated(false); ex = e; //remember a problem happened loading the session
session.getSessionData().copy(data); LOG.warn(e);
session.didActivate(); doDelete(id); //remove the placeholder
phsLock.close();
session = null;
break;
}
} }
else else
session = null; //TODO rely on the expiry mechanism to get rid of it? {
//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
{
//check the session returned
try (Lock lock = session.lock())
{
//is it a placeholder? or is it passivated? In both cases, chuck it away and start again
if (session.isPassivated() || session instanceof PlaceHolderSession)
{
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; return session;
} }
@ -301,12 +392,20 @@ public abstract class AbstractSessionStore extends AbstractLifeCycle implements
if (id == null || session == null) if (id == null || session == null)
throw new IllegalArgumentException ("Put key="+id+" session="+(session==null?"null":session.getId())); 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 //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()) 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()) if (_sessionDataStore.isPassivating())
{ {
@ -322,12 +421,12 @@ public abstract class AbstractSessionStore extends AbstractLifeCycle implements
} }
else else
_sessionDataStore.store(id, session.getSessionData()); _sessionDataStore.store(id, session.getSessionData());
}
} }
doPutIfAbsent(id,session); doPutIfAbsent(id,session);
} }
}
/** /**
* Check to see if the session object exists in this store. * Check to see if the session object exists in this store.
@ -344,60 +443,28 @@ public abstract class AbstractSessionStore extends AbstractLifeCycle implements
/** /**
* Remove a session object from this store and from any backing store. * 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) * @see org.eclipse.jetty.server.session.SessionStore#delete(java.lang.String)
*/ */
@Override @Override
public Session delete(String id) throws Exception public Session delete(String id) throws Exception
{ {
//Ensure that the session object is not passivated so that its attributes //get the session, if its not in memory, this will load it
//are valid Session session = get(id, false);
Session session = doGet(id);
//TODO if (session == null) do we want to load it to delete it? //Always delete it from the backing data store
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
if (_sessionDataStore != null) if (_sessionDataStore != null)
{ {
boolean dsdel = _sessionDataStore.delete(id); boolean dsdel = _sessionDataStore.delete(id);
if (LOG.isDebugEnabled()) LOG.debug("Session {} deleted in db {}",id, dsdel); if (LOG.isDebugEnabled()) LOG.debug("Session {} deleted in db {}",id, dsdel);
} }
//delete it from the session object store
return doDelete(id); 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 * If the SessionDataStore supports passivation,
* sessions that have not be accessed for longer than x sec * write the session to the backing data store.
* *
* @param id identity of session to passivate * @param id identity of session to passivate
*/ */
@ -453,12 +520,8 @@ public abstract class AbstractSessionStore extends AbstractLifeCycle implements
if (!isStarted()) if (!isStarted())
return; return;
if (_sessionDataStore == null) if (_sessionDataStore == null || !_sessionDataStore.isPassivating())
return; //no data store to passivate return; //no data store to passivate or it doesn't passivate
if (!_sessionDataStore.isPassivating())
return; //doesn't support passivation
//get the session locally //get the session locally
Session s = doGet(id); Session s = doGet(id);
@ -470,23 +533,35 @@ public abstract class AbstractSessionStore extends AbstractLifeCycle implements
} }
//lock the session during passivation
try (Lock lock = s.lock()) try (Lock lock = s.lock())
{ {
//check the session is still idle first //check the session is still idle and that it doesn't have requests using it
if (s.isValid() && s.isIdleLongerThan(_idlePassivationTimeoutSec)) if (s.isValid() && s.isIdleLongerThan(_idlePassivationTimeoutSec) && s.isActive() && (s.getRequests() <= 0))
{ {
//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(); s.willPassivate();
_sessionDataStore.store(id, s.getSessionData()); _sessionDataStore.store(id, s.getSessionData());
s.getSessionData().clearAllAttributes();
s.getSessionData().setDirty(false); s.getSessionData().setDirty(false);
} s.setPassivated();
doDelete(id); //Take the session object of this session store
} }
catch (Exception e) catch (Exception e)
{ {
LOG.warn("Passivation of idle session {} failed", id, e); LOG.warn("Passivation of idle session {} failed", id, e);
// TODO should do session.invalidate(); ??? s.setPassivated(); //set it as passivated so it can't be used
doDelete(id); //detach it
} }
} }
}
}

View File

@ -27,13 +27,20 @@ package org.eclipse.jetty.server.session;
*/ */
public class FileSessionManager extends SessionManager public class FileSessionManager extends SessionManager
{ {
protected FileSessionDataStore _sessionDataStore = new FileSessionDataStore(); protected FileSessionDataStore _sessionDataStore;
/**
*
*/
public FileSessionManager ()
{
_sessionStore = new MemorySessionStore();
_sessionDataStore = new FileSessionDataStore();
}
@Override @Override
public void doStart() throws Exception public void doStart() throws Exception
{ {
_sessionStore = new MemorySessionStore();
((AbstractSessionStore)_sessionStore).setSessionDataStore(_sessionDataStore); ((AbstractSessionStore)_sessionStore).setSessionDataStore(_sessionDataStore);
super.doStart(); super.doStart();

View File

@ -81,6 +81,8 @@ public class IdleInspector implements SessionInspector
public void preInspection() public void preInspection()
{ {
_idleCandidates = new HashSet<String>(); _idleCandidates = new HashSet<String>();
if (LOG.isDebugEnabled())
LOG.debug("IdleInspector preinspection");
} }
@ -91,6 +93,9 @@ public class IdleInspector implements SessionInspector
@Override @Override
public void postInspection() public void postInspection()
{ {
if (LOG.isDebugEnabled())
LOG.debug("IdleInspector postinspection");
for (String id:_idleCandidates) for (String id:_idleCandidates)
{ {
try try

View File

@ -717,7 +717,6 @@ public class JDBCSessionDataStore extends AbstractSessionDataStore
//ensure this runs with context classloader set //ensure this runs with context classloader set
_context.run(r); _context.run(r);
if (exception.get() != null) if (exception.get() != null)
throw exception.get(); throw exception.get();

View File

@ -124,7 +124,7 @@ public class MemorySessionStore extends AbstractSessionStore
public Session doPutIfAbsent(String id, Session session) public Session doPutIfAbsent(String id, Session session)
{ {
Session s = _sessions.putIfAbsent(id, session); Session s = _sessions.putIfAbsent(id, session);
if (s == null) if (s == null && !(session instanceof PlaceHolderSession))
_stats.increment(); _stats.increment();
return s; return s;
} }
@ -145,32 +145,12 @@ public class MemorySessionStore extends AbstractSessionStore
public Session doDelete(String id) public Session doDelete(String id)
{ {
Session s = _sessions.remove(id); Session s = _sessions.remove(id);
if (s != null) if (s != null && !(s instanceof PlaceHolderSession))
_stats.decrement(); _stats.decrement();
return s; return s;
} }
/*
@Override
public Set<String> doGetExpiredCandidates()
{
Set<String> candidates = new HashSet<String>();
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;
}
} }

View File

@ -51,12 +51,28 @@ public class Session implements SessionManager.SessionIf
private final static Logger LOG = Log.getLogger("org.eclipse.jetty.server.session"); private final static Logger LOG = Log.getLogger("org.eclipse.jetty.server.session");
/**
*
*/
public final static String SESSION_CREATED_SECURE="org.eclipse.jetty.security.sessionCreatedSecure"; public final static String SESSION_CREATED_SECURE="org.eclipse.jetty.security.sessionCreatedSecure";
/**
* State
*
* Validity states of a session
*/
public enum State {VALID, INVALID, INVALIDATING}; 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 SessionData _sessionData;
protected SessionManager _manager; protected SessionManager _manager;
protected String _extendedId; //the _id plus the worker name protected String _extendedId; //the _id plus the worker name
@ -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 State _state = State.VALID; //state of the session:valid,invalid or being invalidated
private Locker _lock = new Locker(); 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) public Session (HttpServletRequest request, SessionData data)
{ {
_sessionData = data; _sessionData = data;
_newSession = true; _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) public Session (SessionData data)
{ {
_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) public void setSessionManager (SessionManager manager)
{ {
_manager = manager; _manager = manager;
@ -98,7 +142,7 @@ public class Session implements SessionManager.SessionIf
/* ------------------------------------------------------------- */ /* ------------------------------------------------------------- */
protected void cookieSet() protected void cookieSet()
{ {
try (Lock lock = lock()) try (Lock lock = _lock.lockIfNotHeld())
{ {
_sessionData.setCookieSet(_sessionData.getAccessed()); _sessionData.setCookieSet(_sessionData.getAccessed());
} }
@ -106,7 +150,7 @@ public class Session implements SessionManager.SessionIf
/* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */
protected boolean access(long time) protected boolean access(long time)
{ {
try (Lock lock=lock()) try (Lock lock = _lock.lockIfNotHeld())
{ {
if (!isValid()) if (!isValid())
return false; return false;
@ -129,7 +173,7 @@ public class Session implements SessionManager.SessionIf
/* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */
protected void complete() protected void complete()
{ {
try (Lock lock = lock()) try (Lock lock = _lock.lockIfNotHeld())
{ {
_requests--; _requests--;
} }
@ -363,7 +407,7 @@ public class Session implements SessionManager.SessionIf
@Override @Override
public void setMaxInactiveInterval(int secs) public void setMaxInactiveInterval(int secs)
{ {
try (Lock lock = lock()) try (Lock lock = _lock.lockIfNotHeld())
{ {
_sessionData.setMaxInactiveMs((long)secs*1000L); _sessionData.setMaxInactiveMs((long)secs*1000L);
_sessionData.setExpiry(_sessionData.getMaxInactiveMs() <= 0 ? 0 : (System.currentTimeMillis() + _sessionData.getMaxInactiveMs()*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 protected void checkValidForWrite() throws IllegalStateException
{ {
if (!_lock.isLocked()) checkLocked();
throw new IllegalStateException();
if (_state != State.VALID) if (_state != State.VALID)
throw new IllegalStateException(); 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 protected void checkValidForRead () throws IllegalStateException
{ {
if (!_lock.isLocked()) checkLocked();
throw new IllegalStateException();
if (_state == State.INVALID) 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) * @see javax.servlet.http.HttpSession#getAttribute(java.lang.String)
@ -750,7 +808,7 @@ public class Session implements SessionManager.SessionIf
/* ------------------------------------------------------------- */ /* ------------------------------------------------------------- */
public void setIdChanged(boolean changed) public void setIdChanged(boolean changed)
{ {
try (Lock lock = lock()) try (Lock lock = _lock.lockIfNotHeld())
{ {
_idChanged=changed; _idChanged=changed;
} }
@ -785,24 +843,45 @@ public class Session implements SessionManager.SessionIf
} }
/* ------------------------------------------------------------- */
/**
*
*/
public void setPassivated ()
{
checkLocked();
_passivationState = PassivationState.PASSIVATED;
}
/**
*
*/
public void setActive ()
{
checkLocked();
_passivationState = PassivationState.ACTIVE;
}
/**
* @return
*/
public boolean isActive ()
{
checkLocked();
return _passivationState == PassivationState.ACTIVE;
}
/** /**
* @return * @return
*/ */
public boolean isPassivated () public boolean isPassivated ()
{ {
return _isPassivated; checkLocked();
return _passivationState == PassivationState.PASSIVATED;
} }
/* ------------------------------------------------------------- */
/**
* @param isPassivated
*/
public void setPassivated(boolean isPassivated)
{
_isPassivated = isPassivated;
}
} }

View File

@ -53,6 +53,7 @@ import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.statistic.CounterStatistic; import org.eclipse.jetty.util.statistic.CounterStatistic;
import org.eclipse.jetty.util.statistic.SampleStatistic; import org.eclipse.jetty.util.statistic.SampleStatistic;
import org.eclipse.jetty.util.thread.Locker.Lock;
@ -221,10 +222,10 @@ public class SessionManager extends ContainerLifeCycle implements org.eclipse.je
public void complete(HttpSession session) public void complete(HttpSession session)
{ {
Session s = ((SessionIf)session).getSession(); Session s = ((SessionIf)session).getSession();
s.complete();
try try
{ {
if (s.isValid()) s.complete();
_sessionStore.put(s.getId(), s); _sessionStore.put(s.getId(), s);
} }
catch (Exception e) catch (Exception e)
@ -574,6 +575,7 @@ public class SessionManager extends ContainerLifeCycle implements org.eclipse.je
@Override @Override
public HttpSession newHttpSession(HttpServletRequest request) public HttpSession newHttpSession(HttpServletRequest request)
{ {
long created=System.currentTimeMillis(); long created=System.currentTimeMillis();
String id =_sessionIdManager.newSessionId(request,created); String id =_sessionIdManager.newSessionId(request,created);
Session session = _sessionStore.newSession(request, id, created, (_dftMaxIdleSecs>0?_dftMaxIdleSecs*1000L:-1)); 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)); session.getSessionData().setExpiry(_dftMaxIdleSecs <= 0 ? 0 : (created + _dftMaxIdleSecs*1000L));
if (request.isSecure()) if (request.isSecure())
session.setAttribute(Session.SESSION_CREATED_SECURE, Boolean.TRUE); session.setAttribute(Session.SESSION_CREATED_SECURE, Boolean.TRUE);
try try
{ {
_sessionStore.put(id, session); _sessionStore.put(id, session);

View File

@ -18,8 +18,6 @@
package org.eclipse.jetty.server.handler; package org.eclipse.jetty.server.handler;
import static org.hamcrest.Matchers.is;
import java.io.File; import java.io.File;
import java.io.FileWriter; import java.io.FileWriter;
import java.io.InputStream; import java.io.InputStream;
@ -38,7 +36,9 @@ import org.junit.BeforeClass;
import org.junit.Ignore; import org.junit.Ignore;
import org.junit.Test; import org.junit.Test;
@Ignore("Unfixed range bug") import static org.hamcrest.Matchers.is;
@Ignore("Unfixed range bug - Issue #107")
public class ResourceHandlerRangeTest public class ResourceHandlerRangeTest
{ {
private static Server server; private static Server server;

View File

@ -123,6 +123,16 @@ public class SessionCookieTest
return null; 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;
}
} }

View File

@ -1113,6 +1113,8 @@ public class StandardDescriptorProcessor extends IterativeDescriptorProcessor
code=Integer.valueOf(error); code=Integer.valueOf(error);
String location = node.getString("location", false, true); String location = node.getString("location", false, true);
if (!location.startsWith("/"))
throw new IllegalStateException("Missing leading '/' for location: " + location);
ErrorPageErrorHandler handler = (ErrorPageErrorHandler)context.getErrorHandler(); ErrorPageErrorHandler handler = (ErrorPageErrorHandler)context.getErrorHandler();
String originName = "error."+error; String originName = "error."+error;
switch (context.getMetaData().getOrigin(originName)) switch (context.getMetaData().getOrigin(originName))

View File

@ -18,6 +18,11 @@
package org.eclipse.jetty.server.session; 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.File;
import org.eclipse.jetty.server.SessionIdManager; 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) public FileTestServer(int port)
{ {

View File

@ -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);
}
}

View File

@ -37,7 +37,6 @@ import com.google.gcloud.datastore.DatastoreFactory;
public class GCloudTestServer extends AbstractTestServer public class GCloudTestServer extends AbstractTestServer
{ {
static int __workers=0; static int __workers=0;
public static int STALE_INTERVAL_SEC = 1;
@ -82,9 +81,6 @@ public class GCloudTestServer extends AbstractTestServer
GCloudSessionManager sessionManager = new GCloudSessionManager(); GCloudSessionManager sessionManager = new GCloudSessionManager();
sessionManager.setSessionIdManager((GCloudSessionIdManager)_sessionIdManager); sessionManager.setSessionIdManager((GCloudSessionIdManager)_sessionIdManager);
sessionManager.getSessionDataStore().setGCloudConfiguration(((GCloudSessionIdManager)_sessionIdManager).getConfig()); sessionManager.getSessionDataStore().setGCloudConfiguration(((GCloudSessionIdManager)_sessionIdManager).getConfig());
StalePeriodStrategy staleStrategy = new StalePeriodStrategy();
staleStrategy.setStaleSec(STALE_INTERVAL_SEC);
((AbstractSessionStore)sessionManager.getSessionStore()).setStaleStrategy(staleStrategy);
return sessionManager; return sessionManager;
} }

View File

@ -19,10 +19,12 @@
package org.eclipse.jetty.gcloud.session; 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.AbstractInvalidationSessionTest;
import org.eclipse.jetty.server.session.AbstractTestServer; import org.eclipse.jetty.server.session.AbstractTestServer;
import org.junit.AfterClass; import org.junit.AfterClass;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.junit.Ignore;
/** /**
* InvalidationSessionTest * InvalidationSessionTest
@ -32,6 +34,7 @@ import org.junit.BeforeClass;
public class InvalidationSessionTest extends AbstractInvalidationSessionTest public class InvalidationSessionTest extends AbstractInvalidationSessionTest
{ {
static GCloudSessionTestSupport _testSupport; static GCloudSessionTestSupport _testSupport;
public static final int IDLE_PASSIVATE_SEC = 3;
@BeforeClass @BeforeClass
public static void setup () throws Exception public static void setup () throws Exception
@ -50,9 +53,23 @@ public class InvalidationSessionTest extends AbstractInvalidationSessionTest
* @see org.eclipse.jetty.server.session.AbstractInvalidationSessionTest#createServer(int) * @see org.eclipse.jetty.server.session.AbstractInvalidationSessionTest#createServer(int)
*/ */
@Override @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. //has expired on node2 for it to reload the session and discover it has been deleted.
try try
{ {
Thread.currentThread().sleep((2*GCloudTestServer.STALE_INTERVAL_SEC)*1000); Thread.currentThread().sleep((2*IDLE_PASSIVATE_SEC)*1000);
} }
catch (Exception e) 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();
}
} }

View File

@ -26,6 +26,8 @@ import org.junit.AfterClass;
import org.junit.Before; import org.junit.Before;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
import org.junit.Assert;
/** /**
* SessionExpiryTest * SessionExpiryTest
@ -76,21 +78,21 @@ public class SessionExpiryTest extends AbstractSessionExpiryTest
public void testSessionExpiry() throws Exception public void testSessionExpiry() throws Exception
{ {
super.testSessionExpiry(); super.testSessionExpiry();
_testSupport.assertSessions(0); try{_testSupport.assertSessions(0);}catch(Exception e){ Assert.fail(e.getMessage());}
} }
@Override @Override
public void verifySessionCreated(TestHttpSessionListener listener, String sessionId) public void verifySessionCreated(TestHttpSessionListener listener, String sessionId)
{ {
super.verifySessionCreated(listener, 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 @Override
public void verifySessionDestroyed(TestHttpSessionListener listener, String sessionId) public void verifySessionDestroyed(TestHttpSessionListener listener, String sessionId)
{ {
super.verifySessionDestroyed(listener, 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());}
} }

View File

@ -22,6 +22,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import java.io.File; import java.io.File;
import java.io.IOException; 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.IO;
import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.StdErrLog; import org.eclipse.jetty.util.log.StdErrLog;
import org.eclipse.jetty.util.thread.Locker.Lock;
import org.junit.Test; 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. * 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 public class IdleSessionTest
@ -69,9 +71,9 @@ public class IdleSessionTest
@Override @Override
public SessionManager newSessionManager() public SessionManager newSessionManager()
{ {
HashSessionManager manager = (HashSessionManager)super.newSessionManager(); FileSessionManager manager = new FileSessionManager();
//manager.getSessionDataStore().setStoreDir(_storeDir); manager.getSessionStore().setIdlePassivationTimeoutSec(_idlePeriod);
//manager.setIdleSavePeriod(_idlePeriod); manager.getSessionDataStore().setStoreDir(_storeDir);
return manager; return manager;
} }
@ -98,6 +100,7 @@ public class IdleSessionTest
} }
} }
@Test
public void testSessionIdle() throws Exception public void testSessionIdle() throws Exception
{ {
String contextPath = ""; String contextPath = "";
@ -136,7 +139,7 @@ public class IdleSessionTest
pause(idlePeriod * 2); pause(idlePeriod * 2);
//check that the file exists //check that the file exists
checkSessionIdled(storeDir, getSessionId(sessionCookie)); checkSessionIdled(storeDir, HashTestServer.extractSessionId(sessionCookie));
//make another request to de-idle the session //make another request to de-idle the session
Request request = client.newRequest(url + "?action=test"); Request request = client.newRequest(url + "?action=test");
@ -151,11 +154,11 @@ public class IdleSessionTest
pause(idlePeriod * 2); pause(idlePeriod * 2);
//check that it is //check that it is
checkSessionIdled(storeDir, getSessionId(sessionCookie)); checkSessionIdled(storeDir, HashTestServer.extractSessionId(sessionCookie));
//delete the file //delete the file
File idleFile = getIdleFile(storeDir, getSessionId(sessionCookie)); File idleFile = getIdleFile(storeDir, HashTestServer.extractSessionId(sessionCookie));
assertTrue(idleFile.exists()); assertTrue(idleFile.exists());
assertTrue(idleFile.delete()); assertTrue(idleFile.delete());
@ -173,6 +176,10 @@ public class IdleSessionTest
} }
/**
* @param sessionDir
* @param sessionId
*/
public void checkSessionIdled (File sessionDir, String sessionId) public void checkSessionIdled (File sessionDir, String sessionId)
{ {
assertNotNull(sessionDir); assertNotNull(sessionDir);
@ -180,10 +187,13 @@ public class IdleSessionTest
String[] files = sessionDir.list(); String[] files = sessionDir.list();
assertNotNull(files); assertNotNull(files);
assertEquals(1, files.length); assertEquals(1, files.length);
assertEquals(sessionId, files[0]); assertTrue(files[0].contains(sessionId));
} }
/**
* @param sessionDir
*/
public void checkSessionDeIdled (File sessionDir) public void checkSessionDeIdled (File sessionDir)
{ {
assertNotNull(sessionDir); assertNotNull(sessionDir);
@ -193,6 +203,11 @@ public class IdleSessionTest
assertEquals(0, files.length); assertEquals(0, files.length);
} }
/**
* @param sessionDir
* @param sessionId
* @return
*/
public File getIdleFile (File sessionDir, String sessionId) public File getIdleFile (File sessionDir, String sessionId)
{ {
assertNotNull(sessionDir); assertNotNull(sessionDir);
@ -202,13 +217,6 @@ public class IdleSessionTest
return new File(sessionDir, files[0]); 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 public static class TestServlet extends HttpServlet
{ {
@ -223,7 +231,11 @@ public class IdleSessionTest
HttpSession session = request.getSession(true); HttpSession session = request.getSession(true);
session.setAttribute("test", "test"); session.setAttribute("test", "test");
originalId = session.getId(); originalId = session.getId();
// assertTrue(!((HashedSession)session).isIdled()); Session s = (Session)session;
try (Lock lock = s.lock())
{
assertTrue(!s.isPassivated());
}
} }
else if ("test".equals(action)) else if ("test".equals(action))
{ {
@ -231,7 +243,6 @@ public class IdleSessionTest
assertTrue(session != null); assertTrue(session != null);
assertTrue(originalId.equals(session.getId())); assertTrue(originalId.equals(session.getId()));
assertEquals("test", session.getAttribute("test")); assertEquals("test", session.getAttribute("test"));
// assertTrue(!((HashedSession)session).isIdled());
} }
else if ("testfail".equals(action)) else if ("testfail".equals(action))
{ {

View File

@ -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();
}
}

View File

@ -65,9 +65,6 @@ public class InfinispanTestSessionServer extends AbstractTestServer
InfinispanSessionManager sessionManager = new InfinispanSessionManager(); InfinispanSessionManager sessionManager = new InfinispanSessionManager();
sessionManager.setSessionIdManager((InfinispanSessionIdManager)_sessionIdManager); sessionManager.setSessionIdManager((InfinispanSessionIdManager)_sessionIdManager);
sessionManager.getSessionDataStore().setCache(((InfinispanSessionIdManager)_sessionIdManager).getCache()); sessionManager.getSessionDataStore().setCache(((InfinispanSessionIdManager)_sessionIdManager).getCache());
StalePeriodStrategy staleStrategy = new StalePeriodStrategy();
staleStrategy.setStaleSec(1);
((AbstractSessionStore)sessionManager.getSessionStore()).setStaleStrategy(staleStrategy);
return sessionManager; return sessionManager;
} }

View File

@ -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)
{
}
}
}

View File

@ -19,11 +19,14 @@
package org.eclipse.jetty.server.session.remote; 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.AbstractInvalidationSessionTest;
import org.eclipse.jetty.server.session.AbstractTestServer; import org.eclipse.jetty.server.session.AbstractTestServer;
import org.eclipse.jetty.server.session.InfinispanTestSessionServer; import org.eclipse.jetty.server.session.InfinispanTestSessionServer;
import org.eclipse.jetty.session.infinispan.InfinispanSessionManager;
import org.junit.AfterClass; import org.junit.AfterClass;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.junit.Ignore;
/** /**
* InvalidationSessionTest * InvalidationSessionTest
@ -34,7 +37,7 @@ public class RemoteInvalidationSessionTest extends AbstractInvalidationSessionTe
{ {
public static RemoteInfinispanTestSupport __testSupport; 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) * @see org.eclipse.jetty.server.session.AbstractInvalidationSessionTest#createServer(int)
*/ */
@Override @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 @Override
public void testInvalidation() throws Exception 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. //that the node will re-load the session from the database and discover that it has gone.
try try
{ {
Thread.sleep(2 * __staleSec * 1000); Thread.sleep(2 * IDLE_PASSIVATE_SEC * 1000);
} }
catch (InterruptedException e) catch (InterruptedException e)
{ {

View File

@ -18,6 +18,7 @@
package org.eclipse.jetty.server.session; package org.eclipse.jetty.server.session;
import org.eclipse.jetty.server.SessionManager;
import org.junit.After; import org.junit.After;
import org.junit.Test; import org.junit.Test;
@ -26,9 +27,26 @@ import org.junit.Test;
*/ */
public class InvalidationSessionTest extends AbstractInvalidationSessionTest 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() 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. //that the node will re-load the session from the database and discover that it has gone.
try try
{ {
Thread.sleep(2 * JdbcTestServer.STALE_INTERVAL * 1000); Thread.sleep(2 * IDLE_PASSIVATE_SEC * 1000);
} }
catch (InterruptedException e) catch (InterruptedException e)
{ {

View File

@ -137,7 +137,7 @@ public class JdbcTestServer extends AbstractTestServer
JDBCSessionManager manager = new JDBCSessionManager(); JDBCSessionManager manager = new JDBCSessionManager();
manager.setSessionIdManager((JDBCSessionIdManager)_sessionIdManager); manager.setSessionIdManager((JDBCSessionIdManager)_sessionIdManager);
JDBCSessionDataStore ds = manager.getSessionDataStore(); JDBCSessionDataStore ds = manager.getSessionDataStore();
ds.setGracePeriodSec(_scavengePeriod); ds.setGracePeriodSec(_inspectionPeriod);
manager.getDatabaseAdaptor().setDriverInfo(DRIVER_CLASS, DEFAULT_CONNECTION_URL); manager.getDatabaseAdaptor().setDriverInfo(DRIVER_CLASS, DEFAULT_CONNECTION_URL);
JDBCSessionDataStore.SessionTableSchema sessionTableSchema = new JDBCSessionDataStore.SessionTableSchema(); JDBCSessionDataStore.SessionTableSchema sessionTableSchema = new JDBCSessionDataStore.SessionTableSchema();
sessionTableSchema.setTableName(TABLE); sessionTableSchema.setTableName(TABLE);
@ -153,9 +153,6 @@ public class JdbcTestServer extends AbstractTestServer
sessionTableSchema.setMapColumn(MAP_COL); sessionTableSchema.setMapColumn(MAP_COL);
sessionTableSchema.setMaxIntervalColumn(MAX_IDLE_COL); sessionTableSchema.setMaxIntervalColumn(MAX_IDLE_COL);
ds.setSessionTableSchema(sessionTableSchema); ds.setSessionTableSchema(sessionTableSchema);
StalePeriodStrategy staleStrategy = new StalePeriodStrategy();
staleStrategy.setStaleSec(STALE_INTERVAL);
((AbstractSessionStore)manager.getSessionStore()).setStaleStrategy(staleStrategy);
return manager; return manager;
} }

View File

@ -21,6 +21,7 @@ package org.eclipse.jetty.server.session;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import static org.junit.Assert.assertFalse;
import java.io.File; import java.io.File;
import java.io.FileWriter; import java.io.FileWriter;
@ -125,8 +126,10 @@ public class ReloadedSessionMissingClassTest
response = request.send(); response = request.send();
assertEquals(HttpServletResponse.SC_OK,response.getStatus()); assertEquals(HttpServletResponse.SC_OK,response.getStatus());
String afterStopSessionId = (String)webApp.getServletContext().getAttribute("foo.session"); String afterStopSessionId = (String)webApp.getServletContext().getAttribute("foo.session");
Boolean fooPresent = (Boolean)webApp.getServletContext().getAttribute("foo.present");
assertFalse(fooPresent);
assertNotNull(afterStopSessionId); assertNotNull(afterStopSessionId);
assertFalse(fooPresent);
assertTrue(!afterStopSessionId.equals(sessionId)); assertTrue(!afterStopSessionId.equals(sessionId));
} }

View File

@ -67,9 +67,9 @@ public class SaveIntervalTest
holder.setServlet(servlet); holder.setServlet(servlet);
ctxA.addServlet(holder, "/test"); ctxA.addServlet(holder, "/test");
StalePeriodStrategy strategy = new StalePeriodStrategy();
strategy.setStaleSec(SAVE); //TODO set up the intermittent save
((AbstractSessionStore)((JDBCSessionManager)ctxA.getSessionHandler().getSessionManager()).getSessionStore()).setStaleStrategy(strategy);
server.start(); server.start();
int port=server.getPort(); int port=server.getPort();
try try

View File

@ -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());
}
}
}

View File

@ -19,6 +19,7 @@
package org.eclipse.jetty.nosql.mongodb; 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.AbstractInvalidationSessionTest;
import org.eclipse.jetty.server.session.AbstractTestServer; import org.eclipse.jetty.server.session.AbstractTestServer;
import org.junit.AfterClass; import org.junit.AfterClass;
@ -27,7 +28,7 @@ import org.junit.Test;
public class InvalidateSessionTest extends AbstractInvalidationSessionTest public class InvalidateSessionTest extends AbstractInvalidationSessionTest
{ {
public final static int IDLE_PASSIVATE_SEC = 1;
@BeforeClass @BeforeClass
public static void beforeClass() throws Exception public static void beforeClass() throws Exception
@ -43,9 +44,24 @@ public class InvalidateSessionTest extends AbstractInvalidationSessionTest
} }
@Override @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 @Override
@ -53,7 +69,7 @@ public class InvalidateSessionTest extends AbstractInvalidationSessionTest
{ {
try try
{ {
Thread.sleep(2 * MongoTestServer.STALE_INTERVAL * 1000); Thread.sleep(2 * IDLE_PASSIVATE_SEC * 1000);
} }
catch (InterruptedException e) catch (InterruptedException e)
{ {

View File

@ -22,10 +22,8 @@ import java.net.UnknownHostException;
import org.eclipse.jetty.server.SessionIdManager; import org.eclipse.jetty.server.SessionIdManager;
import org.eclipse.jetty.server.SessionManager; 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.AbstractTestServer;
import org.eclipse.jetty.server.session.SessionHandler; import org.eclipse.jetty.server.session.SessionHandler;
import org.eclipse.jetty.server.session.StalePeriodStrategy;
import com.mongodb.DBCollection; import com.mongodb.DBCollection;
import com.mongodb.Mongo; import com.mongodb.Mongo;
@ -37,7 +35,6 @@ import com.mongodb.MongoException;
*/ */
public class MongoTestServer extends AbstractTestServer public class MongoTestServer extends AbstractTestServer
{ {
public static final int STALE_INTERVAL = 1;
static int __workers=0; static int __workers=0;
@ -98,10 +95,7 @@ public class MongoTestServer extends AbstractTestServer
try try
{ {
manager = new MongoSessionManager(); manager = new MongoSessionManager();
manager.getSessionDataStore().setGracePeriodSec(_scavengePeriod); manager.getSessionDataStore().setGracePeriodSec(_inspectionPeriod);
StalePeriodStrategy staleStrategy = new StalePeriodStrategy();
staleStrategy.setStaleSec(STALE_INTERVAL);
((AbstractSessionStore)manager.getSessionStore()).setStaleStrategy(staleStrategy);
} }
catch (Exception e) catch (Exception e)
{ {

View File

@ -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;
}
}
}
}

View File

@ -43,7 +43,7 @@ import org.junit.Test;
*/ */
public abstract class AbstractInvalidationSessionTest public abstract class AbstractInvalidationSessionTest
{ {
public abstract AbstractTestServer createServer(int port); public abstract AbstractTestServer createServer(int port, int maxInactive, int inspectInterval);
public abstract void pause(); public abstract void pause();
@Test @Test
@ -51,7 +51,7 @@ public abstract class AbstractInvalidationSessionTest
{ {
String contextPath = ""; String contextPath = "";
String servletMapping = "/server"; String servletMapping = "/server";
AbstractTestServer server1 = createServer(0); AbstractTestServer server1 = createServer(0, 30, 1);
server1.addContext(contextPath).addServlet(TestServlet.class, servletMapping); server1.addContext(contextPath).addServlet(TestServlet.class, servletMapping);
@ -59,7 +59,7 @@ public abstract class AbstractInvalidationSessionTest
{ {
server1.start(); server1.start();
int port1 = server1.getPort(); int port1 = server1.getPort();
AbstractTestServer server2 = createServer(0); AbstractTestServer server2 = createServer(0, 30, 1);
server2.addContext(contextPath).addServlet(TestServlet.class, servletMapping); server2.addContext(contextPath).addServlet(TestServlet.class, servletMapping);
try try

View File

@ -35,14 +35,14 @@ import org.eclipse.jetty.webapp.WebAppContext;
public abstract class AbstractTestServer public abstract class AbstractTestServer
{ {
public static int DEFAULT_MAX_INACTIVE = 30; 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 Server _server;
protected final int _maxInactivePeriod; protected final int _maxInactivePeriod;
protected final int _scavengePeriod; protected final int _inspectionPeriod;
protected final ContextHandlerCollection _contexts; protected final ContextHandlerCollection _contexts;
protected SessionIdManager _sessionIdManager; protected SessionIdManager _sessionIdManager;
private PeriodicSessionInspector _scavenger; private PeriodicSessionInspector _inspector;
@ -66,7 +66,7 @@ public abstract class AbstractTestServer
public AbstractTestServer(int port) 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) public AbstractTestServer(int port, int maxInactivePeriod, int scavengePeriod)
@ -78,14 +78,14 @@ public abstract class AbstractTestServer
{ {
_server = new Server(port); _server = new Server(port);
_maxInactivePeriod = maxInactivePeriod; _maxInactivePeriod = maxInactivePeriod;
_scavengePeriod = scavengePeriod; _inspectionPeriod = scavengePeriod;
_contexts = new ContextHandlerCollection(); _contexts = new ContextHandlerCollection();
_sessionIdManager = newSessionIdManager(sessionIdMgrConfig); _sessionIdManager = newSessionIdManager(sessionIdMgrConfig);
_server.setSessionIdManager(_sessionIdManager); _server.setSessionIdManager(_sessionIdManager);
((AbstractSessionIdManager) _sessionIdManager).setServer(_server); ((AbstractSessionIdManager) _sessionIdManager).setServer(_server);
_scavenger = new PeriodicSessionInspector(); _inspector = new PeriodicSessionInspector();
_scavenger.setIntervalSec(scavengePeriod); _inspector.setIntervalSec(scavengePeriod);
((AbstractSessionIdManager)_sessionIdManager).setSessionScavenger(_scavenger); ((AbstractSessionIdManager)_sessionIdManager).setSessionScavenger(_inspector);
} }
@ -102,6 +102,11 @@ public abstract class AbstractTestServer
_server.start(); _server.start();
} }
public PeriodicSessionInspector getInspector()
{
return _inspector;
}
public int getPort() public int getPort()
{ {
return ((NetworkConnector)getServer().getConnectors()[0]).getLocalPort(); return ((NetworkConnector)getServer().getConnectors()[0]).getLocalPort();