374258 - SPDY leaks SSLEngines
This commit is contained in:
parent
4a02fdb6c0
commit
ab9bcf26b2
|
@ -80,7 +80,9 @@ public class Promise<T> implements Handler<T>, Future<T>
|
||||||
@Override
|
@Override
|
||||||
public T get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException
|
public T get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException
|
||||||
{
|
{
|
||||||
latch.await(timeout, unit);
|
boolean elapsed = !latch.await(timeout, unit);
|
||||||
|
if (elapsed)
|
||||||
|
throw new TimeoutException();
|
||||||
return result();
|
return result();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -38,6 +38,7 @@ import java.util.concurrent.Executors;
|
||||||
import java.util.concurrent.Future;
|
import java.util.concurrent.Future;
|
||||||
import java.util.concurrent.RejectedExecutionException;
|
import java.util.concurrent.RejectedExecutionException;
|
||||||
import java.util.concurrent.ScheduledExecutorService;
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
import javax.net.ssl.SSLEngine;
|
import javax.net.ssl.SSLEngine;
|
||||||
import javax.net.ssl.SSLException;
|
import javax.net.ssl.SSLException;
|
||||||
|
|
||||||
|
@ -313,7 +314,7 @@ public class SPDYClient
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AsyncConnection newConnection(final SocketChannel channel, AsyncEndPoint endPoint, final Object attachment)
|
public AsyncConnection newConnection(final SocketChannel channel, AsyncEndPoint endPoint, Object attachment)
|
||||||
{
|
{
|
||||||
SessionPromise sessionPromise = (SessionPromise)attachment;
|
SessionPromise sessionPromise = (SessionPromise)attachment;
|
||||||
final SPDYClient client = sessionPromise.client;
|
final SPDYClient client = sessionPromise.client;
|
||||||
|
@ -322,11 +323,31 @@ public class SPDYClient
|
||||||
{
|
{
|
||||||
if (sslContextFactory != null)
|
if (sslContextFactory != null)
|
||||||
{
|
{
|
||||||
|
final AtomicReference<AsyncEndPoint> sslEndPointRef = new AtomicReference<>();
|
||||||
|
final AtomicReference<Object> attachmentRef = new AtomicReference<>(attachment);
|
||||||
SSLEngine engine = client.newSSLEngine(sslContextFactory, channel);
|
SSLEngine engine = client.newSSLEngine(sslContextFactory, channel);
|
||||||
SslConnection sslConnection = new SslConnection(engine, endPoint);
|
SslConnection sslConnection = new SslConnection(engine, endPoint)
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void onClose()
|
||||||
|
{
|
||||||
|
sslEndPointRef.set(null);
|
||||||
|
attachmentRef.set(null);
|
||||||
|
super.onClose();
|
||||||
|
}
|
||||||
|
};
|
||||||
endPoint.setConnection(sslConnection);
|
endPoint.setConnection(sslConnection);
|
||||||
final AsyncEndPoint sslEndPoint = sslConnection.getSslEndPoint();
|
AsyncEndPoint sslEndPoint = sslConnection.getSslEndPoint();
|
||||||
|
sslEndPointRef.set(sslEndPoint);
|
||||||
|
|
||||||
|
// Instances of the ClientProvider inner class strong reference the
|
||||||
|
// SslEndPoint (via lexical scoping), which strong references the SSLEngine.
|
||||||
|
// Since NextProtoNego stores in a WeakHashMap the SSLEngine as key
|
||||||
|
// and this instance as value, we are in the situation where the value
|
||||||
|
// of a WeakHashMap refers indirectly to the key, which is bad because
|
||||||
|
// the entry will never be removed from the WeakHashMap.
|
||||||
|
// We use AtomicReferences to be captured via lexical scoping,
|
||||||
|
// and we null them out above when the connection is closed.
|
||||||
NextProtoNego.put(engine, new NextProtoNego.ClientProvider()
|
NextProtoNego.put(engine, new NextProtoNego.ClientProvider()
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
|
@ -340,7 +361,8 @@ public class SPDYClient
|
||||||
{
|
{
|
||||||
// Server does not support NPN, but this is a SPDY client, so hardcode SPDY
|
// Server does not support NPN, but this is a SPDY client, so hardcode SPDY
|
||||||
ClientSPDYAsyncConnectionFactory connectionFactory = new ClientSPDYAsyncConnectionFactory();
|
ClientSPDYAsyncConnectionFactory connectionFactory = new ClientSPDYAsyncConnectionFactory();
|
||||||
AsyncConnection connection = connectionFactory.newAsyncConnection(channel, sslEndPoint, attachment);
|
AsyncEndPoint sslEndPoint = sslEndPointRef.get();
|
||||||
|
AsyncConnection connection = connectionFactory.newAsyncConnection(channel, sslEndPoint, attachmentRef.get());
|
||||||
sslEndPoint.setConnection(connection);
|
sslEndPoint.setConnection(connection);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -352,9 +374,9 @@ public class SPDYClient
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
AsyncConnectionFactory connectionFactory = client.getAsyncConnectionFactory(protocol);
|
AsyncConnectionFactory connectionFactory = client.getAsyncConnectionFactory(protocol);
|
||||||
AsyncConnection connection = connectionFactory.newAsyncConnection(channel, sslEndPoint, attachment);
|
AsyncEndPoint sslEndPoint = sslEndPointRef.get();
|
||||||
|
AsyncConnection connection = connectionFactory.newAsyncConnection(channel, sslEndPoint, attachmentRef.get());
|
||||||
sslEndPoint.setConnection(connection);
|
sslEndPoint.setConnection(connection);
|
||||||
|
|
||||||
return protocol;
|
return protocol;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -29,6 +29,7 @@ import java.util.concurrent.Executor;
|
||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.Executors;
|
||||||
import java.util.concurrent.ScheduledExecutorService;
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
import javax.net.ssl.SSLEngine;
|
import javax.net.ssl.SSLEngine;
|
||||||
import javax.net.ssl.SSLException;
|
import javax.net.ssl.SSLException;
|
||||||
|
|
||||||
|
@ -176,16 +177,35 @@ public class SPDYServerConnector extends SelectChannelConnector
|
||||||
if (sslContextFactory != null)
|
if (sslContextFactory != null)
|
||||||
{
|
{
|
||||||
SSLEngine engine = newSSLEngine(sslContextFactory, channel);
|
SSLEngine engine = newSSLEngine(sslContextFactory, channel);
|
||||||
SslConnection sslConnection = new SslConnection(engine, endPoint);
|
final AtomicReference<AsyncEndPoint> sslEndPointRef = new AtomicReference<>();
|
||||||
|
SslConnection sslConnection = new SslConnection(engine, endPoint)
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void onClose()
|
||||||
|
{
|
||||||
|
sslEndPointRef.set(null);
|
||||||
|
super.onClose();
|
||||||
|
}
|
||||||
|
};
|
||||||
endPoint.setConnection(sslConnection);
|
endPoint.setConnection(sslConnection);
|
||||||
final AsyncEndPoint sslEndPoint = sslConnection.getSslEndPoint();
|
AsyncEndPoint sslEndPoint = sslConnection.getSslEndPoint();
|
||||||
|
sslEndPointRef.set(sslEndPoint);
|
||||||
|
|
||||||
|
// Instances of the ServerProvider inner class strong reference the
|
||||||
|
// SslEndPoint (via lexical scoping), which strong references the SSLEngine.
|
||||||
|
// Since NextProtoNego stores in a WeakHashMap the SSLEngine as key
|
||||||
|
// and this instance as value, we are in the situation where the value
|
||||||
|
// of a WeakHashMap refers indirectly to the key, which is bad because
|
||||||
|
// the entry will never be removed from the WeakHashMap.
|
||||||
|
// We use AtomicReferences to be captured via lexical scoping,
|
||||||
|
// and we null them out above when the connection is closed.
|
||||||
NextProtoNego.put(engine, new NextProtoNego.ServerProvider()
|
NextProtoNego.put(engine, new NextProtoNego.ServerProvider()
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
public void unsupported()
|
public void unsupported()
|
||||||
{
|
{
|
||||||
AsyncConnectionFactory connectionFactory = getDefaultAsyncConnectionFactory();
|
AsyncConnectionFactory connectionFactory = getDefaultAsyncConnectionFactory();
|
||||||
|
AsyncEndPoint sslEndPoint = sslEndPointRef.get();
|
||||||
AsyncConnection connection = connectionFactory.newAsyncConnection(channel, sslEndPoint, SPDYServerConnector.this);
|
AsyncConnection connection = connectionFactory.newAsyncConnection(channel, sslEndPoint, SPDYServerConnector.this);
|
||||||
sslEndPoint.setConnection(connection);
|
sslEndPoint.setConnection(connection);
|
||||||
}
|
}
|
||||||
|
@ -200,6 +220,7 @@ public class SPDYServerConnector extends SelectChannelConnector
|
||||||
public void protocolSelected(String protocol)
|
public void protocolSelected(String protocol)
|
||||||
{
|
{
|
||||||
AsyncConnectionFactory connectionFactory = getAsyncConnectionFactory(protocol);
|
AsyncConnectionFactory connectionFactory = getAsyncConnectionFactory(protocol);
|
||||||
|
AsyncEndPoint sslEndPoint = sslEndPointRef.get();
|
||||||
AsyncConnection connection = connectionFactory.newAsyncConnection(channel, sslEndPoint, SPDYServerConnector.this);
|
AsyncConnection connection = connectionFactory.newAsyncConnection(channel, sslEndPoint, SPDYServerConnector.this);
|
||||||
sslEndPoint.setConnection(connection);
|
sslEndPoint.setConnection(connection);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,48 @@
|
||||||
|
package org.eclipse.jetty.spdy;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import org.eclipse.jetty.npn.NextProtoNego;
|
||||||
|
import org.eclipse.jetty.spdy.api.Session;
|
||||||
|
import org.eclipse.jetty.spdy.api.server.ServerSessionFrameListener;
|
||||||
|
import org.eclipse.jetty.util.ssl.SslContextFactory;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
public class SSLEngineLeakTest extends AbstractTest
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
protected SPDYServerConnector newSPDYServerConnector(ServerSessionFrameListener listener)
|
||||||
|
{
|
||||||
|
SslContextFactory sslContextFactory = newSslContextFactory();
|
||||||
|
return new SPDYServerConnector(listener, sslContextFactory);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected SPDYClient.Factory newSPDYClientFactory(Executor threadPool)
|
||||||
|
{
|
||||||
|
SslContextFactory sslContextFactory = newSslContextFactory();
|
||||||
|
return new SPDYClient.Factory(threadPool, sslContextFactory);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSSLEngineLeak() throws Exception
|
||||||
|
{
|
||||||
|
avoidStackLocalVariables();
|
||||||
|
Thread.sleep(1000);
|
||||||
|
System.gc();
|
||||||
|
Field field = NextProtoNego.class.getDeclaredField("objects");
|
||||||
|
field.setAccessible(true);
|
||||||
|
Map<Object, NextProtoNego.Provider> objects = (Map<Object, NextProtoNego.Provider>)field.get(null);
|
||||||
|
Assert.assertEquals(0, objects.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void avoidStackLocalVariables() throws Exception
|
||||||
|
{
|
||||||
|
Session session = startClient(startServer(null), null);
|
||||||
|
session.goAway().get(5, TimeUnit.SECONDS);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue