Merge remote-tracking branch 'origin/jetty-9.4.x' into jetty-10.0.x

This commit is contained in:
Jan Bartel 2018-11-23 16:45:32 +01:00
commit 506e4dc682
8 changed files with 665 additions and 335 deletions

View File

@ -476,9 +476,7 @@
<systemPropertyVariables>
<mavenRepoPath>${settings.localRepository}</mavenRepoPath>
</systemPropertyVariables>
<excludes>
<exclude>**/TestJettyOSGiBootHTTP2</exclude>
</excludes>
<argLine>-Dconscrypt-version=${conscrypt.version}</argLine>
</configuration>
<!-- paxexam still using junit 4 so we have to force the provider here -->
<dependencies>

View File

@ -100,9 +100,9 @@ public class TestJettyOSGiBootHTTP2Conscrypt
res.add(CoreOptions.systemProperty("jetty.alpn.protocols").value("h2,http/1.1"));
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,*")
.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")
.start());
res.add(mavenBundle().groupId("org.eclipse.jetty.osgi").artifactId("jetty-osgi-alpn").versionAsInProject().noStart());

View File

@ -151,8 +151,9 @@ public abstract class AbstractSessionCache extends ContainerLifeCycle implements
/**
* Remove the session with this identity from the store
*
* @param id the id
* @return true if removed false otherwise
* @return Session that was removed or null
*/
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
public Session renewSessionId (String oldId, String newId)
public Session renewSessionId (String oldId, String newId, String oldExtendedId, String newExtendedId)
throws Exception
{
if (StringUtil.isBlank(oldId))
@ -741,17 +738,40 @@ public abstract class AbstractSessionCache extends ContainerLifeCycle implements
throw new IllegalArgumentException ("New session id is null");
Session session = get(oldId);
if (session == null)
return null;
renewSessionId(session, newId, newExtendedId);
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())
{
String oldId = session.getId();
session.checkValidForWrite(); //can't change id on invalid session
session.getSessionData().setId(newId);
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
doDelete (oldId); //take old out of map
if (_sessionDataStore != null)
{
_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())
LOG.debug ("Session id {} swapped for new id {}", oldId, newId);
return session;
}
}

View File

@ -91,16 +91,41 @@ public interface SessionCache extends LifeCycle
*/
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.
*
* @param oldId the current 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
* @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);
}
/**

View File

@ -18,6 +18,8 @@
package org.eclipse.jetty.server.session;
import static java.lang.Math.round;
import java.io.IOException;
import java.util.Arrays;
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.Scheduler;
import static java.lang.Math.round;
/* ------------------------------------------------------------ */
/**
* SessionHandler.
@ -343,6 +343,60 @@ public class SessionHandler extends ScopedHandler
_sessionListeners.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);
_sessionsCreatedStats.increment();
if (request.isSecure())
if (request!=null && request.isSecure())
session.setAttribute(Session.SESSION_CREATED_SECURE, Boolean.TRUE);
if (_sessionListeners!=null)
{
HttpSessionEvent event=new HttpSessionEvent(session);
for (HttpSessionListener listener : _sessionListeners)
listener.sessionCreated(event);
}
callSessionCreatedListeners(session);
return session;
}
@ -1183,24 +1232,15 @@ public class SessionHandler extends ScopedHandler
{
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)
{
//session doesn't exist on this context
return;
}
session.setExtendedId(newExtendedId); //remember the extended id
//inform the listeners
if (!_sessionIdListeners.isEmpty())
{
HttpSessionEvent event = new HttpSessionEvent(session);
for (HttpSessionIdListener l:_sessionIdListeners)
{
l.sessionIdChanged(event, oldId);
}
}
callSessionIdListeners(session, oldId);
}
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)
{
if (StringUtil.isBlank(id))
return;
try
{
//remove the session and call the destroy listeners
Session session = removeSession(id, true);
{
// Remove the Session object from the session cache and any backing
// data store
Session session = _sessionCache.delete(id);
if (session != null)
{
_sessionTimeStats.record(round((System.currentTimeMillis() - session.getSessionData().getCreated())/1000.0));
session.finishInvalidate();
//start invalidating if it is not already begun, and call the listeners
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)

View File

@ -99,7 +99,7 @@ public interface Dumpable
}
catch (Throwable th)
{
out.append("=>").append(th.toString()).append("\n");
out.append("=> ").append(th.toString()).append("\n");
}
}
@ -133,12 +133,12 @@ public interface Dumpable
for (Iterator<Object> i = container.getBeans().iterator(); i.hasNext();)
{
Object bean = i.next();
String nextIndent = indent + ((i.hasNext() || size>0) ? "| " : " ");
String nextIndent = indent + ((i.hasNext() || size>0) ? "| " : " ");
if (bean instanceof LifeCycle)
{
if (container.isManaged(bean))
{
out.append(indent).append("+=");
out.append(indent).append("+= ");
if (bean instanceof Dumpable)
((Dumpable)bean).dump(out,nextIndent);
else
@ -146,7 +146,7 @@ public interface Dumpable
}
else if (containerLifeCycle != null && containerLifeCycle.isAuto(bean))
{
out.append(indent).append("+?");
out.append(indent).append("+? ");
if (bean instanceof Dumpable)
((Dumpable)bean).dump(out,nextIndent);
else
@ -154,18 +154,18 @@ public interface Dumpable
}
else
{
out.append(indent).append("+~");
out.append(indent).append("+~ ");
dumpObject(out, bean);
}
}
else if (containerLifeCycle != null && containerLifeCycle.isUnmanaged(bean))
{
out.append(indent).append("+~");
out.append(indent).append("+~ ");
dumpObject(out, bean);
}
else
{
out.append(indent).append("+-");
out.append(indent).append("+- ");
if (bean instanceof Dumpable)
((Dumpable)bean).dump(out,nextIndent);
else
@ -178,8 +178,8 @@ public interface Dumpable
for (Iterator i = ((Iterable<?>)object).iterator(); i.hasNext();)
{
Object item = i.next();
String nextIndent = indent + ((i.hasNext() || size>0) ? "| " : " ");
out.append(indent).append("+:");
String nextIndent = indent + ((i.hasNext() || size>0) ? "| " : " ");
out.append(indent).append("+: ");
if (item instanceof Dumpable)
((Dumpable)item).dump(out,nextIndent);
else
@ -191,8 +191,8 @@ public interface Dumpable
for (Iterator<? extends Map.Entry<?, ?>> i = ((Map<?,?>)object).entrySet().iterator(); i.hasNext();)
{
Map.Entry entry = i.next();
String nextIndent = indent + ((i.hasNext() || size>0) ? "| " : " ");
out.append(indent).append("+@").append(String.valueOf(entry.getKey())).append('=');
String nextIndent = indent + ((i.hasNext() || size>0) ? "| " : " ");
out.append(indent).append("+@ ").append(String.valueOf(entry.getKey())).append('=');
Object item = entry.getValue();
if (item instanceof Dumpable)
((Dumpable)item).dump(out,nextIndent);
@ -208,13 +208,12 @@ public interface Dumpable
for (Object item : extraChildren)
{
i++;
String nextIndent = indent + (i<size ? "| " : " ");
out.append(indent).append("+>");
String nextIndent = indent + (i<size ? "| " : " ");
out.append(indent).append("+> ");
if (item instanceof Dumpable)
((Dumpable)item).dump(out,nextIndent);
else
dumpObjects(out, nextIndent, item);
}
}
}

View File

@ -34,7 +34,9 @@ import java.util.concurrent.TimeUnit;
import javax.servlet.http.HttpSessionActivationListener;
import javax.servlet.http.HttpSessionEvent;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.SessionIdManager;
import org.eclipse.jetty.servlet.ServletContextHandler;
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.
@ -180,7 +276,7 @@ public class DefaultSessionCacheTest
cache.put("1234", session);
assertTrue(cache.contains("1234"));
cache.renewSessionId("1234", "5678");
cache.renewSessionId("1234", "5678", "1234.foo", "5678.foo");
assertTrue(cache.contains("5678"));
assertFalse(cache.contains("1234"));