Merge branch 'jetty-9.4.x' of github.com:eclipse/jetty.project into jetty-9.4.x
This commit is contained in:
commit
97cb063a57
|
@ -601,6 +601,7 @@
|
||||||
<excludes>
|
<excludes>
|
||||||
<exclude>**/TestJettyOSGiBootHTTP2</exclude>
|
<exclude>**/TestJettyOSGiBootHTTP2</exclude>
|
||||||
</excludes>
|
</excludes>
|
||||||
|
<argLine>-Dconscrypt-version=${conscrypt.version}</argLine>
|
||||||
</configuration>
|
</configuration>
|
||||||
</plugin>
|
</plugin>
|
||||||
</plugins>
|
</plugins>
|
||||||
|
|
|
@ -100,9 +100,9 @@ public class TestJettyOSGiBootHTTP2Conscrypt
|
||||||
res.add(CoreOptions.systemProperty("jetty.alpn.protocols").value("h2,http/1.1"));
|
res.add(CoreOptions.systemProperty("jetty.alpn.protocols").value("h2,http/1.1"));
|
||||||
res.add(CoreOptions.systemProperty("jetty.sslContext.provider").value("Conscrypt"));
|
res.add(CoreOptions.systemProperty("jetty.sslContext.provider").value("Conscrypt"));
|
||||||
|
|
||||||
res.add(wrappedBundle(mavenBundle().groupId("org.conscrypt").artifactId("conscrypt-openjdk-uber").version("1.1.4"))
|
res.add(wrappedBundle(mavenBundle().groupId("org.conscrypt").artifactId("conscrypt-openjdk-uber").versionAsInProject())
|
||||||
.imports("javax.net.ssl,*")
|
.imports("javax.net.ssl,*")
|
||||||
.exports("org.conscrypt;version=1.1.4")
|
.exports("org.conscrypt;version="+System.getProperty("conscrypt-version"))
|
||||||
.instructions("Bundle-NativeCode=META-INF/native/libconscrypt_openjdk_jni-linux-x86_64.so")
|
.instructions("Bundle-NativeCode=META-INF/native/libconscrypt_openjdk_jni-linux-x86_64.so")
|
||||||
.start());
|
.start());
|
||||||
res.add(mavenBundle().groupId("org.eclipse.jetty.osgi").artifactId("jetty-osgi-alpn").versionAsInProject().noStart());
|
res.add(mavenBundle().groupId("org.eclipse.jetty.osgi").artifactId("jetty-osgi-alpn").versionAsInProject().noStart());
|
||||||
|
|
|
@ -151,8 +151,9 @@ public abstract class AbstractSessionCache extends ContainerLifeCycle implements
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove the session with this identity from the store
|
* Remove the session with this identity from the store
|
||||||
|
*
|
||||||
* @param id the id
|
* @param id the id
|
||||||
* @return true if removed false otherwise
|
* @return Session that was removed or null
|
||||||
*/
|
*/
|
||||||
public abstract Session doDelete (String id);
|
public abstract Session doDelete (String id);
|
||||||
|
|
||||||
|
@ -727,12 +728,8 @@ public abstract class AbstractSessionCache extends ContainerLifeCycle implements
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @see org.eclipse.jetty.server.session.SessionCache#renewSessionId(java.lang.String, java.lang.String)
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public Session renewSessionId (String oldId, String newId)
|
public Session renewSessionId (String oldId, String newId, String oldExtendedId, String newExtendedId)
|
||||||
throws Exception
|
throws Exception
|
||||||
{
|
{
|
||||||
if (StringUtil.isBlank(oldId))
|
if (StringUtil.isBlank(oldId))
|
||||||
|
@ -741,17 +738,40 @@ public abstract class AbstractSessionCache extends ContainerLifeCycle implements
|
||||||
throw new IllegalArgumentException ("New session id is null");
|
throw new IllegalArgumentException ("New session id is null");
|
||||||
|
|
||||||
Session session = get(oldId);
|
Session session = get(oldId);
|
||||||
if (session == null)
|
renewSessionId(session, newId, newExtendedId);
|
||||||
return null;
|
|
||||||
|
|
||||||
|
return session;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Swap the id on a session.
|
||||||
|
*
|
||||||
|
* @param session the session for which to do the swap
|
||||||
|
* @param newId the new id
|
||||||
|
* @param newExtendedId the full id plus node id
|
||||||
|
*
|
||||||
|
* @throws Exception if there was a failure saving the change
|
||||||
|
*/
|
||||||
|
protected void renewSessionId (Session session, String newId, String newExtendedId)
|
||||||
|
throws Exception
|
||||||
|
{
|
||||||
|
if (session == null)
|
||||||
|
return;
|
||||||
|
|
||||||
try (Lock lock = session.lock())
|
try (Lock lock = session.lock())
|
||||||
{
|
{
|
||||||
|
String oldId = session.getId();
|
||||||
session.checkValidForWrite(); //can't change id on invalid session
|
session.checkValidForWrite(); //can't change id on invalid session
|
||||||
session.getSessionData().setId(newId);
|
session.getSessionData().setId(newId);
|
||||||
session.getSessionData().setLastSaved(0); //pretend that the session has never been saved before to get a full save
|
session.getSessionData().setLastSaved(0); //pretend that the session has never been saved before to get a full save
|
||||||
session.getSessionData().setDirty(true); //ensure we will try to write the session out
|
session.getSessionData().setDirty(true); //ensure we will try to write the session out
|
||||||
|
session.setExtendedId(newExtendedId); //remember the new extended id
|
||||||
|
session.setIdChanged(true); //session id changed
|
||||||
|
|
||||||
doPutIfAbsent(newId, session); //put the new id into our map
|
doPutIfAbsent(newId, session); //put the new id into our map
|
||||||
doDelete (oldId); //take old out of map
|
doDelete (oldId); //take old out of map
|
||||||
|
|
||||||
if (_sessionDataStore != null)
|
if (_sessionDataStore != null)
|
||||||
{
|
{
|
||||||
_sessionDataStore.delete(oldId); //delete the session data with the old id
|
_sessionDataStore.delete(oldId); //delete the session data with the old id
|
||||||
|
@ -759,7 +779,6 @@ public abstract class AbstractSessionCache extends ContainerLifeCycle implements
|
||||||
}
|
}
|
||||||
if (LOG.isDebugEnabled())
|
if (LOG.isDebugEnabled())
|
||||||
LOG.debug ("Session id {} swapped for new id {}", oldId, newId);
|
LOG.debug ("Session id {} swapped for new id {}", oldId, newId);
|
||||||
return session;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -91,16 +91,41 @@ public interface SessionCache extends LifeCycle
|
||||||
*/
|
*/
|
||||||
Session newSession (SessionData data);
|
Session newSession (SessionData data);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Change the id of a session.
|
||||||
|
*
|
||||||
|
* This method has been superceded by the 4 arg renewSessionId method and
|
||||||
|
* should no longer be called.
|
||||||
|
*
|
||||||
|
* @param oldId the old id
|
||||||
|
* @param newId the new id
|
||||||
|
* @return the changed Session
|
||||||
|
* @throws Exception if anything went wrong
|
||||||
|
* @deprecated use
|
||||||
|
* {@link #renewSessionId(String oldId, String newId, String oldExtendedId, String newExtendedId)}
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
default Session renewSessionId(String oldId, String newId) throws Exception
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Change the id of a Session.
|
* Change the id of a Session.
|
||||||
*
|
*
|
||||||
* @param oldId the current session id
|
* @param oldId the current session id
|
||||||
* @param newId the new session id
|
* @param newId the new session id
|
||||||
|
* @param oldExtendedId the current extended session id
|
||||||
|
* @param newExtendedId the new extended session id
|
||||||
* @return the Session after changing its id
|
* @return the Session after changing its id
|
||||||
* @throws Exception if any error occurred
|
* @throws Exception if any error occurred
|
||||||
*/
|
*/
|
||||||
Session renewSessionId (String oldId, String newId) throws Exception;
|
default Session renewSessionId(String oldId, String newId, String oldExtendedId, String newExtendedId) throws Exception
|
||||||
|
{
|
||||||
|
return renewSessionId(oldId, newId);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -18,6 +18,8 @@
|
||||||
|
|
||||||
package org.eclipse.jetty.server.session;
|
package org.eclipse.jetty.server.session;
|
||||||
|
|
||||||
|
import static java.lang.Math.round;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
@ -65,8 +67,6 @@ import org.eclipse.jetty.util.thread.Locker.Lock;
|
||||||
import org.eclipse.jetty.util.thread.ScheduledExecutorScheduler;
|
import org.eclipse.jetty.util.thread.ScheduledExecutorScheduler;
|
||||||
import org.eclipse.jetty.util.thread.Scheduler;
|
import org.eclipse.jetty.util.thread.Scheduler;
|
||||||
|
|
||||||
import static java.lang.Math.round;
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
/**
|
/**
|
||||||
* SessionHandler.
|
* SessionHandler.
|
||||||
|
@ -343,6 +343,60 @@ public class SessionHandler extends ScopedHandler
|
||||||
_sessionListeners.clear();
|
_sessionListeners.clear();
|
||||||
_sessionIdListeners.clear();
|
_sessionIdListeners.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Call the session lifecycle listeners
|
||||||
|
* @param session the session on which to call the lifecycle listeners
|
||||||
|
*/
|
||||||
|
protected void callSessionDestroyedListeners (Session session)
|
||||||
|
{
|
||||||
|
if (session == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (_sessionListeners!=null)
|
||||||
|
{
|
||||||
|
HttpSessionEvent event=new HttpSessionEvent(session);
|
||||||
|
for (int i = _sessionListeners.size()-1; i>=0; i--)
|
||||||
|
{
|
||||||
|
_sessionListeners.get(i).sessionDestroyed(event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Call the session lifecycle listeners
|
||||||
|
* @param session the session on which to call the lifecycle listeners
|
||||||
|
*/
|
||||||
|
protected void callSessionCreatedListeners (Session session)
|
||||||
|
{
|
||||||
|
if (session == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (_sessionListeners!=null)
|
||||||
|
{
|
||||||
|
HttpSessionEvent event=new HttpSessionEvent(session);
|
||||||
|
for (int i = _sessionListeners.size()-1; i>=0; i--)
|
||||||
|
{
|
||||||
|
_sessionListeners.get(i).sessionCreated(event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected void callSessionIdListeners (Session session, String oldId)
|
||||||
|
{
|
||||||
|
//inform the listeners
|
||||||
|
if (!_sessionIdListeners.isEmpty())
|
||||||
|
{
|
||||||
|
HttpSessionEvent event = new HttpSessionEvent(session);
|
||||||
|
for (HttpSessionIdListener l:_sessionIdListeners)
|
||||||
|
{
|
||||||
|
l.sessionIdChanged(event, oldId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
/**
|
/**
|
||||||
|
@ -793,15 +847,10 @@ public class SessionHandler extends ScopedHandler
|
||||||
_sessionCache.put(id, session);
|
_sessionCache.put(id, session);
|
||||||
_sessionsCreatedStats.increment();
|
_sessionsCreatedStats.increment();
|
||||||
|
|
||||||
if (request.isSecure())
|
if (request!=null && request.isSecure())
|
||||||
session.setAttribute(Session.SESSION_CREATED_SECURE, Boolean.TRUE);
|
session.setAttribute(Session.SESSION_CREATED_SECURE, Boolean.TRUE);
|
||||||
|
|
||||||
if (_sessionListeners!=null)
|
callSessionCreatedListeners(session);
|
||||||
{
|
|
||||||
HttpSessionEvent event=new HttpSessionEvent(session);
|
|
||||||
for (HttpSessionListener listener : _sessionListeners)
|
|
||||||
listener.sessionCreated(event);
|
|
||||||
}
|
|
||||||
|
|
||||||
return session;
|
return session;
|
||||||
}
|
}
|
||||||
|
@ -1183,24 +1232,15 @@ public class SessionHandler extends ScopedHandler
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Session session = _sessionCache.renewSessionId (oldId, newId); //swap the id over
|
Session session = _sessionCache.renewSessionId (oldId, newId, oldExtendedId, newExtendedId); //swap the id over
|
||||||
if (session == null)
|
if (session == null)
|
||||||
{
|
{
|
||||||
//session doesn't exist on this context
|
//session doesn't exist on this context
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
session.setExtendedId(newExtendedId); //remember the extended id
|
|
||||||
|
|
||||||
//inform the listeners
|
//inform the listeners
|
||||||
if (!_sessionIdListeners.isEmpty())
|
callSessionIdListeners(session, oldId);
|
||||||
{
|
|
||||||
HttpSessionEvent event = new HttpSessionEvent(session);
|
|
||||||
for (HttpSessionIdListener l:_sessionIdListeners)
|
|
||||||
{
|
|
||||||
l.sessionIdChanged(event, oldId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
|
@ -1208,28 +1248,62 @@ public class SessionHandler extends ScopedHandler
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Record length of time session has been active. Called when the
|
||||||
|
* session is about to be invalidated.
|
||||||
|
*
|
||||||
|
* @param session the session whose time to record
|
||||||
|
*/
|
||||||
|
protected void recordSessionTime (Session session)
|
||||||
|
{
|
||||||
|
_sessionTimeStats.record(round((System.currentTimeMillis() - session.getSessionData().getCreated())/1000.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
/**
|
/**
|
||||||
* Called when a session has expired.
|
* Called by SessionIdManager to remove a session that has been invalidated,
|
||||||
|
* either by this context or another context. Also called by
|
||||||
|
* SessionIdManager when a session has expired in either this context or
|
||||||
|
* another context.
|
||||||
*
|
*
|
||||||
* @param id the id to invalidate
|
* @param id the session id to invalidate
|
||||||
*/
|
*/
|
||||||
public void invalidate (String id)
|
public void invalidate (String id)
|
||||||
{
|
{
|
||||||
|
|
||||||
if (StringUtil.isBlank(id))
|
if (StringUtil.isBlank(id))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
//remove the session and call the destroy listeners
|
// Remove the Session object from the session cache and any backing
|
||||||
Session session = removeSession(id, true);
|
// data store
|
||||||
|
Session session = _sessionCache.delete(id);
|
||||||
if (session != null)
|
if (session != null)
|
||||||
{
|
{
|
||||||
_sessionTimeStats.record(round((System.currentTimeMillis() - session.getSessionData().getCreated())/1000.0));
|
//start invalidating if it is not already begun, and call the listeners
|
||||||
session.finishInvalidate();
|
try
|
||||||
|
{
|
||||||
|
if (session.beginInvalidate())
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
callSessionDestroyedListeners(session);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
LOG.warn("Session listener threw exception", e);
|
||||||
|
}
|
||||||
|
//call the attribute removed listeners and finally mark it as invalid
|
||||||
|
session.finishInvalidate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (IllegalStateException e)
|
||||||
|
{
|
||||||
|
if (LOG.isDebugEnabled()) LOG.debug("Session {} already invalid", session);
|
||||||
|
LOG.ignore(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
|
|
|
@ -34,7 +34,9 @@ import java.util.concurrent.TimeUnit;
|
||||||
import javax.servlet.http.HttpSessionActivationListener;
|
import javax.servlet.http.HttpSessionActivationListener;
|
||||||
import javax.servlet.http.HttpSessionEvent;
|
import javax.servlet.http.HttpSessionEvent;
|
||||||
|
|
||||||
|
import org.eclipse.jetty.server.Request;
|
||||||
import org.eclipse.jetty.server.Server;
|
import org.eclipse.jetty.server.Server;
|
||||||
|
import org.eclipse.jetty.server.SessionIdManager;
|
||||||
import org.eclipse.jetty.servlet.ServletContextHandler;
|
import org.eclipse.jetty.servlet.ServletContextHandler;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
@ -65,6 +67,100 @@ public class DefaultSessionCacheTest
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRenewIdMultipleRequests() throws Exception
|
||||||
|
{
|
||||||
|
//Test that invalidation happens on ALL copies of the session that are in-use by requests
|
||||||
|
Server server = new Server();
|
||||||
|
|
||||||
|
SessionIdManager sessionIdManager = new DefaultSessionIdManager(server);
|
||||||
|
server.setSessionIdManager(sessionIdManager);
|
||||||
|
ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS);
|
||||||
|
context.setContextPath("/test");
|
||||||
|
context.setServer(server);
|
||||||
|
context.getSessionHandler().setMaxInactiveInterval((int)TimeUnit.DAYS.toSeconds(1));
|
||||||
|
context.getSessionHandler().setSessionIdManager(sessionIdManager);
|
||||||
|
|
||||||
|
DefaultSessionCacheFactory cacheFactory = new DefaultSessionCacheFactory();
|
||||||
|
cacheFactory.setSaveOnCreate(true); //ensures that a session is persisted as soon as it is created
|
||||||
|
|
||||||
|
DefaultSessionCache cache = (DefaultSessionCache)cacheFactory.getSessionCache(context.getSessionHandler());
|
||||||
|
|
||||||
|
TestSessionDataStore store = new TestSessionDataStore();
|
||||||
|
cache.setSessionDataStore(store);
|
||||||
|
context.getSessionHandler().setSessionCache(cache);
|
||||||
|
TestHttpSessionListener listener = new TestHttpSessionListener();
|
||||||
|
context.getSessionHandler().addEventListener(listener);
|
||||||
|
|
||||||
|
server.setHandler(context);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
server.start();
|
||||||
|
|
||||||
|
//create a new session
|
||||||
|
Session s = (Session)context.getSessionHandler().newHttpSession(null);
|
||||||
|
String id = s.getId();
|
||||||
|
context.getSessionHandler().access(s, false); //simulate accessing the request
|
||||||
|
context.getSessionHandler().complete(s); //simulate completing the request
|
||||||
|
|
||||||
|
//make 1st request
|
||||||
|
final Session session = context.getSessionHandler().getSession(id); //get the session again
|
||||||
|
assertNotNull(session);
|
||||||
|
context.getSessionHandler().access(session, false); //simulate accessing the request
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//make 2nd request
|
||||||
|
final Session session2 = context.getSessionHandler().getSession(id); //get the session again
|
||||||
|
context.getSessionHandler().access(session2, false); //simulate accessing the request
|
||||||
|
assertNotNull(session2);
|
||||||
|
assertTrue(session == session2);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Thread t2 = new Thread(new Runnable()
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void run()
|
||||||
|
{
|
||||||
|
System.err.println("Starting session id renewal");
|
||||||
|
session2.renewId(new Request(null,null));
|
||||||
|
System.err.println("Finished session id renewal");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
t2.start();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Thread t = new Thread(new Runnable()
|
||||||
|
{
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run()
|
||||||
|
{
|
||||||
|
System.err.println("Starting invalidation");
|
||||||
|
try{Thread.sleep(1000L);}catch (Exception e) {e.printStackTrace();}
|
||||||
|
session.invalidate();
|
||||||
|
System.err.println("Finished invalidation");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
t.start();
|
||||||
|
|
||||||
|
t.join();
|
||||||
|
t2.join();
|
||||||
|
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
server.stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test sessions are saved when shutdown with a store.
|
* Test sessions are saved when shutdown with a store.
|
||||||
|
@ -180,7 +276,7 @@ public class DefaultSessionCacheTest
|
||||||
cache.put("1234", session);
|
cache.put("1234", session);
|
||||||
assertTrue(cache.contains("1234"));
|
assertTrue(cache.contains("1234"));
|
||||||
|
|
||||||
cache.renewSessionId("1234", "5678");
|
cache.renewSessionId("1234", "5678", "1234.foo", "5678.foo");
|
||||||
|
|
||||||
assertTrue(cache.contains("5678"));
|
assertTrue(cache.contains("5678"));
|
||||||
assertFalse(cache.contains("1234"));
|
assertFalse(cache.contains("1234"));
|
||||||
|
|
Loading…
Reference in New Issue