Merged from master.

This commit is contained in:
Simone Bordet 2012-03-14 18:24:57 +01:00
commit 5624a5721e
18 changed files with 322 additions and 64 deletions

View File

@ -5,7 +5,6 @@
<version>8.1.3-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>org.eclipse.jetty.aggregate</groupId>
<artifactId>jetty-all-server</artifactId>
<name>Jetty :: Aggregate :: All Server</name>
<properties>

View File

@ -5,7 +5,6 @@
<version>8.1.3-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>org.eclipse.jetty.aggregate</groupId>
<artifactId>jetty-client</artifactId>
<name>Jetty :: Aggregate :: HTTP Client</name>

View File

@ -1,4 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<artifactId>jetty-project</artifactId>
<groupId>org.eclipse.jetty</groupId>

View File

@ -237,6 +237,9 @@ public class SslConnection extends AbstractConnection implements AsyncConnection
/* ------------------------------------------------------------ */
public void onClose()
{
Connection connection = _sslEndPoint.getConnection();
if (connection != null && connection != this)
connection.onClose();
}
/* ------------------------------------------------------------ */
@ -408,7 +411,7 @@ public class SslConnection extends AbstractConnection implements AsyncConnection
}
// If we are reading into the temp buffer and it has some content, then we should be dispatched.
if (toFill==_unwrapBuf && _unwrapBuf.hasContent())
if (toFill==_unwrapBuf && _unwrapBuf.hasContent() && !_connection.isSuspended())
_aEndp.asyncDispatch();
}
finally
@ -550,7 +553,7 @@ public class SslConnection extends AbstractConnection implements AsyncConnection
break;
case BUFFER_OVERFLOW:
_logger.debug("{} unwrap {} {}->{}",_session,result.getStatus(),_inbound.toDetailString(),buffer.toDetailString());
if (_logger.isDebugEnabled()) _logger.debug("{} unwrap {} {}->{}",_session,result.getStatus(),_inbound.toDetailString(),buffer.toDetailString());
break;
case OK:

View File

@ -24,12 +24,15 @@ import org.eclipse.jetty.server.Request;
* Rewrite the URI by matching with a regular expression.
* The replacement string may use $n" to replace the nth capture group.
* If the replacement string contains ? character, then it is split into a path
* and query string component. The returned target contains only the path.
* and query string component. The replacement query string may also contain $Q, which
* is replaced with the original query string.
* The returned target contains only the path.
*/
public class RewriteRegexRule extends RegexRule implements Rule.ApplyURI
{
private String _replacement;
private String _query;
private boolean _queryGroup;
/* ------------------------------------------------------------ */
public RewriteRegexRule()
@ -49,6 +52,7 @@ public class RewriteRegexRule extends RegexRule implements Rule.ApplyURI
String[] split=replacement.split("\\?",2);
_replacement = split[0];
_query=split.length==2?split[1]:null;
_queryGroup=_query!=null && _query.contains("$Q");
}
@ -73,7 +77,12 @@ public class RewriteRegexRule extends RegexRule implements Rule.ApplyURI
}
if (query!=null)
{
if (_queryGroup)
query=query.replace("$Q",request.getQueryString()==null?"":request.getQueryString());
request.setAttribute("org.eclipse.jetty.rewrite.handler.RewriteRegexRule.Q",query);
}
return target;
}
@ -84,7 +93,7 @@ public class RewriteRegexRule extends RegexRule implements Rule.ApplyURI
if (_query!=null)
{
String query=(String)request.getAttribute("org.eclipse.jetty.rewrite.handler.RewriteRegexRule.Q");
if (request.getQueryString()==null)
if (_queryGroup||request.getQueryString()==null)
request.setQueryString(query);
else
request.setQueryString(request.getQueryString()+"&"+query);

View File

@ -197,28 +197,25 @@ public class RuleContainer extends Rule
if (applied!=null)
{
LOG.debug("applied {}",rule);
if (!target.equals(applied))
{
LOG.debug("rewrote {} to {}",target,applied);
if (!original_set)
{
original_set=true;
request.setAttribute(_originalPathAttribute, target);
}
LOG.debug("rewrote {} to {}",target,applied);
if (!original_set)
{
original_set=true;
request.setAttribute(_originalPathAttribute, target);
}
if (_rewriteRequestURI)
{
if (rule instanceof Rule.ApplyURI && !target.equals(request.getRequestURI()))
((Rule.ApplyURI)rule).applyURI((Request)request, target, applied);
else
((Request)request).setRequestURI(applied);
}
if (_rewritePathInfo)
((Request)request).setPathInfo(applied);
target=applied;
if (_rewriteRequestURI)
{
if (rule instanceof Rule.ApplyURI)
((Rule.ApplyURI)rule).applyURI((Request)request, target, applied);
else
((Request)request).setRequestURI(applied);
}
if (_rewritePathInfo)
((Request)request).setPathInfo(applied);
target=applied;
if (rule.isHandling())
{

View File

@ -23,13 +23,21 @@ public class RewriteRegexRuleTest extends AbstractRuleTestCase
{
private String[][] _tests=
{
{"/foo/bar",".*","/replace","/replace",null},
{"/foo/bar","/xxx.*","/replace",null,null},
{"/foo/bar","/(.*)/(.*)","/$2/$1/xxx","/bar/foo/xxx",null},
{"/foo/bar","/(foo)/(.*)(bar)","/$3/$1/xxx$2","/bar/foo/xxx",null},
{"/foo/$bar",".*","/$replace","/$replace",null},
{"/foo/$bar","/foo/(.*)","/$1/replace","/$bar/replace",null},
{"/foo/bar/info","/foo/(NotHere)?([^/]*)/(.*)","/$3/other?p1=$2","/info/other","p1=bar"},
{"/foo/bar",null,".*","/replace","/replace",null},
{"/foo/bar","n=v",".*","/replace","/replace","n=v"},
{"/foo/bar",null,"/xxx.*","/replace",null,null},
{"/foo/bar",null,"/(.*)/(.*)","/$2/$1/xxx","/bar/foo/xxx",null},
{"/foo/bar",null,"/(.*)/(.*)","/test?p2=$2&p1=$1","/test","p2=bar&p1=foo"},
{"/foo/bar","n=v","/(.*)/(.*)","/test?p2=$2&p1=$1","/test","n=v&p2=bar&p1=foo"},
{"/foo/bar",null,"/(.*)/(.*)","/foo/bar?p2=$2&p1=$1","/foo/bar","p2=bar&p1=foo"},
{"/foo/bar","n=v","/(.*)/(.*)","/foo/bar?p2=$2&p1=$1","/foo/bar","n=v&p2=bar&p1=foo"},
{"/foo/bar",null,"/(foo)/(.*)(bar)","/$3/$1/xxx$2","/bar/foo/xxx",null},
{"/foo/$bar",null,".*","/$replace","/$replace",null},
{"/foo/$bar",null,"/foo/(.*)","/$1/replace","/$bar/replace",null},
{"/foo/bar/info",null,"/foo/(NotHere)?([^/]*)/(.*)","/$3/other?p1=$2","/info/other","p1=bar"},
{"/foo/bar/info",null,"/foo/(NotHere)?([^/]*)/(.*)","/$3/other?p1=$2&$Q","/info/other","p1=bar&"},
{"/foo/bar/info","n=v","/foo/(NotHere)?([^/]*)/(.*)","/$3/other?p1=$2&$Q","/info/other","p1=bar&n=v"},
{"/foo/bar/info","n=v","/foo/(NotHere)?([^/]*)/(.*)","/$3/other?p1=$2","/info/other","n=v&p1=bar"},
};
private RewriteRegexRule _rule;
@ -45,17 +53,43 @@ public class RewriteRegexRuleTest extends AbstractRuleTestCase
{
for (String[] test : _tests)
{
_rule.setRegex(test[1]);
_rule.setReplacement(test[2]);
String result = _rule.matchAndApply(test[0], _request, _response);
assertEquals(test[1], test[3], result);
String t=test[0]+"?"+test[1]+">"+test[2]+"|"+test[3];
_rule.setRegex(test[2]);
_rule.setReplacement(test[3]);
_request.setRequestURI(test[0]);
_request.setQueryString(null);
_request.setQueryString(test[1]);
_request.getAttributes().clearAttributes();
String result = _rule.matchAndApply(test[0], _request, _response);
assertEquals(t, test[4], result);
_rule.applyURI(_request,test[0],result);
assertEquals(test[3], _request.getRequestURI());
assertEquals(test[4], _request.getQueryString());
assertEquals(t,test[4], _request.getRequestURI());
assertEquals(t,test[5], _request.getQueryString());
}
}
@Test
public void testContainedRequestUriEnabled() throws IOException
{
RuleContainer container = new RuleContainer();
container.setRewriteRequestURI(true);
container.addRule(_rule);
for (String[] test : _tests)
{
String t=test[0]+"?"+test[1]+">"+test[2]+"|"+test[3];
_rule.setRegex(test[2]);
_rule.setReplacement(test[3]);
_request.setRequestURI(test[0]);
_request.setQueryString(test[1]);
_request.getAttributes().clearAttributes();
String result = container.apply(test[0],_request,_response);
assertEquals(t,test[4]==null?test[0]:test[4], result);
assertEquals(t,test[4]==null?test[0]:test[4], _request.getRequestURI());
assertEquals(t,test[5], _request.getQueryString());
}
}
}

View File

@ -39,9 +39,11 @@ import org.eclipse.jetty.util.IO;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.StdErrLog;
import org.hamcrest.CoreMatchers;
import org.hamcrest.Matchers;
import org.junit.Test;
import org.junit.matchers.JUnitMatchers;
import org.mockito.internal.matchers.Contains;
import static org.hamcrest.Matchers.greaterThanOrEqualTo;
import static org.junit.Assert.assertEquals;
@ -1280,4 +1282,51 @@ public abstract class HttpServerTestBase extends HttpServerTestFixture
response.setStatus(200);
}
}
@Test
public void testSuspendedPipeline() throws Exception
{
SuspendHandler suspend = new SuspendHandler();
suspend.setSuspendFor(30000);
suspend.setResumeAfter(1000);
configureServer(suspend);
long start=System.currentTimeMillis();
Socket client=newSocket(HOST,_connector.getLocalPort());
try
{
OutputStream os=client.getOutputStream();
InputStream is=client.getInputStream();
// write an initial request
os.write((
"GET / HTTP/1.1\r\n"+
"host: "+HOST+":"+_connector.getLocalPort()+"\r\n"+
"\r\n"
).getBytes());
os.flush();
Thread.sleep(200);
// write an pipelined request
os.write((
"GET / HTTP/1.1\r\n"+
"host: "+HOST+":"+_connector.getLocalPort()+"\r\n"+
"connection: close\r\n"+
"\r\n"
).getBytes());
os.flush();
String response=readResponse(client);
assertThat(response,JUnitMatchers.containsString("RESUMEDHTTP/1.1 200 OK"));
assertThat((System.currentTimeMillis()-start),greaterThanOrEqualTo(1999L));
// TODO This test should also check that that the CPU did not spin during the suspend.
}
finally
{
client.close();
}
}
}

View File

@ -31,6 +31,13 @@ public class SelectChannelServerTest extends HttpServerTestBase
{
super.testCommittedError();
}
@Override
public void testSuspendedPipeline() throws Exception
{
// TODO Auto-generated method stub
super.testSuspendedPipeline();
}
}

View File

@ -144,5 +144,10 @@ public class SelectChannelServerSslTest extends HttpServerTestBase
{
}
@Override
public void testSuspendedPipeline() throws Exception
{
super.testSuspendedPipeline();
}
}

View File

@ -590,7 +590,8 @@ public class ServletHandler extends ScopedHandler
}
finally
{
baseRequest.setHandled(true);
if (servlet_holder!=null)
baseRequest.setHandled(true);
}
}

View File

@ -1007,7 +1007,7 @@ public class DoSFilter implements Filter
if (_rateTrackers != null && _trackerTimeoutQ != null)
{
long now = _trackerTimeoutQ.getNow();
int latestIndex = _next == 0 ? 3 : (_next - 1 ) % _timestamps.length;
int latestIndex = _next == 0 ? (_timestamps.length-1) : (_next - 1 );
long last=_timestamps[latestIndex];
boolean hasRecentRequest = last != 0 && (now-last)<1000L;

View File

@ -80,7 +80,9 @@ public class Promise<T> implements Handler<T>, Future<T>
@Override
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();
}

View File

@ -16,6 +16,11 @@
package org.eclipse.jetty.spdy.http;
import java.io.IOException;
import org.eclipse.jetty.http.HttpSchemes;
import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.spdy.AsyncConnectionFactory;
import org.eclipse.jetty.spdy.SPDYServerConnector;
import org.eclipse.jetty.spdy.api.SPDY;
@ -52,4 +57,34 @@ public class HTTPSPDYServerConnector extends SPDYServerConnector
{
return defaultConnectionFactory;
}
@Override
public void customize(EndPoint endPoint, Request request) throws IOException
{
super.customize(endPoint, request);
if (getSslContextFactory() != null)
request.setScheme(HttpSchemes.HTTPS);
}
@Override
public boolean isConfidential(Request request)
{
if (getSslContextFactory() != null)
{
int confidentialPort = getConfidentialPort();
return confidentialPort == 0 || confidentialPort == request.getServerPort();
}
return super.isConfidential(request);
}
@Override
public boolean isIntegral(Request request)
{
if (getSslContextFactory() != null)
{
int integralPort = getIntegralPort();
return integralPort == 0 || integralPort == request.getServerPort();
}
return super.isIntegral(request);
}
}

View File

@ -31,8 +31,9 @@ import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.continuation.Continuation;
import org.eclipse.jetty.continuation.ContinuationSupport;
import org.eclipse.jetty.io.ByteArrayBuffer;
import javax.servlet.AsyncContext;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.handler.AbstractHandler;
import org.eclipse.jetty.spdy.api.BytesDataInfo;
@ -975,7 +976,10 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
throws IOException, ServletException
{
request.setHandled(true);
final AsyncContext async = request.startAsync();
final Continuation continuation = ContinuationSupport.getContinuation(request);
continuation.suspend();
new Thread()
{
@Override
@ -988,7 +992,7 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
int read = 0;
while (read < data.length)
read += input.read(buffer);
async.complete();
continuation.complete();
latch.countDown();
}
catch (IOException x)
@ -1034,7 +1038,10 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
throws IOException, ServletException
{
request.setHandled(true);
final AsyncContext async = request.startAsync();
final Continuation continuation = ContinuationSupport.getContinuation(request);
continuation.suspend();
new Thread()
{
@Override
@ -1047,7 +1054,7 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
int read = 0;
while (read < 2 * data.length)
read += input.read(buffer);
async.complete();
continuation.complete();
latch.countDown();
}
catch (IOException x)
@ -1094,14 +1101,17 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
throws IOException, ServletException
{
request.setHandled(true);
if (request.getAsyncContinuation().isInitial())
final Continuation continuation = ContinuationSupport.getContinuation(request);
if (continuation.isInitial())
{
InputStream input = request.getInputStream();
byte[] buffer = new byte[256];
int read = 0;
while (read < data.length)
read += input.read(buffer);
final AsyncContext async = request.startAsync();
continuation.suspend();
new Thread()
{
@Override
@ -1110,7 +1120,7 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
try
{
TimeUnit.SECONDS.sleep(1);
async.dispatch();
continuation.resume();
latch.countDown();
}
catch (InterruptedException x)

View File

@ -38,6 +38,7 @@ import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.atomic.AtomicReference;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLException;
@ -313,7 +314,7 @@ public class SPDYClient
}
@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;
final SPDYClient client = sessionPromise.client;
@ -322,11 +323,31 @@ public class SPDYClient
{
if (sslContextFactory != null)
{
final AtomicReference<AsyncEndPoint> sslEndPointRef = new AtomicReference<>();
final AtomicReference<Object> attachmentRef = new AtomicReference<>(attachment);
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);
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()
{
@Override
@ -340,7 +361,8 @@ public class SPDYClient
{
// Server does not support NPN, but this is a SPDY client, so hardcode SPDY
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);
}
@ -352,9 +374,9 @@ public class SPDYClient
return null;
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);
return protocol;
}
});

View File

@ -29,6 +29,7 @@ import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLException;
@ -76,7 +77,7 @@ public class SPDYServerConnector extends SelectChannelConnector
return bufferPool;
}
protected Executor getExecutor()
public Executor getExecutor()
{
final ThreadPool threadPool = getThreadPool();
if (threadPool instanceof Executor)
@ -91,11 +92,16 @@ public class SPDYServerConnector extends SelectChannelConnector
};
}
protected ScheduledExecutorService getScheduler()
public ScheduledExecutorService getScheduler()
{
return scheduler;
}
public SslContextFactory getSslContextFactory()
{
return sslContextFactory;
}
@Override
protected void doStart() throws Exception
{
@ -171,16 +177,35 @@ public class SPDYServerConnector extends SelectChannelConnector
if (sslContextFactory != null)
{
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);
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()
{
@Override
public void unsupported()
{
AsyncConnectionFactory connectionFactory = getDefaultAsyncConnectionFactory();
AsyncEndPoint sslEndPoint = sslEndPointRef.get();
AsyncConnection connection = connectionFactory.newAsyncConnection(channel, sslEndPoint, SPDYServerConnector.this);
sslEndPoint.setConnection(connection);
}
@ -195,6 +220,7 @@ public class SPDYServerConnector extends SelectChannelConnector
public void protocolSelected(String protocol)
{
AsyncConnectionFactory connectionFactory = getAsyncConnectionFactory(protocol);
AsyncEndPoint sslEndPoint = sslEndPointRef.get();
AsyncConnection connection = connectionFactory.newAsyncConnection(channel, sslEndPoint, SPDYServerConnector.this);
sslEndPoint.setConnection(connection);
}

View File

@ -0,0 +1,59 @@
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
{
System.gc();
Thread.sleep(1000);
Field field = NextProtoNego.class.getDeclaredField("objects");
field.setAccessible(true);
Map<Object, NextProtoNego.Provider> objects = (Map<Object, NextProtoNego.Provider>)field.get(null);
int initialSize = objects.size();
avoidStackLocalVariables();
// Allow the close to arrive to the server and the selector to process it
Thread.sleep(1000);
// Perform GC to be sure that the WeakHashMap is cleared
System.gc();
Thread.sleep(1000);
// Check that the WeakHashMap is empty
Assert.assertEquals(initialSize, objects.size());
}
private void avoidStackLocalVariables() throws Exception
{
Session session = startClient(startServer(null), null);
session.goAway().get(5, TimeUnit.SECONDS);
}
}