368821 - SSL Spin on asyncDispatch.

Test (for now ignored) that reproduces the problem.
This commit is contained in:
Simone Bordet 2012-01-17 23:05:41 +01:00
parent 6e38a97d7f
commit e609a65e61
1 changed files with 172 additions and 1 deletions

View File

@ -10,6 +10,7 @@ import java.net.SocketTimeoutException;
import java.nio.channels.SocketChannel;
import java.util.Arrays;
import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
@ -47,6 +48,7 @@ import org.junit.After;
import org.junit.Assert;
import org.junit.Assume;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import static org.hamcrest.Matchers.greaterThan;
@ -61,8 +63,10 @@ public class SslBytesServerTest extends SslBytesTest
private final int idleTimeout = 2000;
private ExecutorService threadPool;
private Server server;
private int serverPort;
private SSLContext sslContext;
private SimpleProxy proxy;
private Runnable idleHook;
@Before
public void init() throws Exception
@ -98,6 +102,15 @@ public class SslBytesServerTest extends SslBytesTest
}
};
}
@Override
public void onIdleExpired(long idleForMs)
{
final Runnable idleHook = SslBytesServerTest.this.idleHook;
if (idleHook != null)
idleHook.run();
super.onIdleExpired(idleForMs);
}
};
}
@ -166,7 +179,7 @@ public class SslBytesServerTest extends SslBytesTest
}
});
server.start();
int serverPort = connector.getLocalPort();
serverPort = connector.getLocalPort();
sslContext = cf.getSslContext();
@ -1512,6 +1525,164 @@ public class SslBytesServerTest extends SslBytesTest
Assert.assertFalse(serverEndPoint.get().isOpen());
}
@Test
public void testPlainText() throws Exception
{
final SSLSocket client = newClient();
threadPool.submit(new Callable<Object>()
{
public Object call() throws Exception
{
client.startHandshake();
return null;
}
});
// Instead of passing the Client Hello, we simulate plain text was passed in
proxy.flushToServer(0, "GET / HTTP/1.1\r\n".getBytes("UTF-8"));
// We expect that the server closes the connection immediately
TLSRecord record = proxy.readFromServer();
Assert.assertNull(String.valueOf(record), record);
// Check that we did not spin
TimeUnit.MILLISECONDS.sleep(500);
Assert.assertThat(sslHandles.get(), lessThan(20));
Assert.assertThat(sslFlushes.get(), lessThan(20));
Assert.assertThat(httpParses.get(), lessThan(50));
client.close();
}
@Ignore
@Test
public void testRequestConcurrentWithIdleExpiration() throws Exception
{
final SSLSocket client = newClient();
final OutputStream clientOutput = client.getOutputStream();
final CountDownLatch latch = new CountDownLatch(1);
idleHook = new Runnable()
{
public void run()
{
try
{
// Send request
clientOutput.write(("" +
"GET / HTTP/1.1\r\n" +
"Host: localhost\r\n" +
"\r\n").getBytes("UTF-8"));
clientOutput.flush();
latch.countDown();
}
catch (Exception x)
{
// Latch won't trigger and test will
// fail, just print the stack trace
x.printStackTrace();
}
}
};
SimpleProxy.AutomaticFlow automaticProxyFlow = proxy.startAutomaticFlow();
client.startHandshake();
Assert.assertTrue(automaticProxyFlow.stop(5, TimeUnit.SECONDS));
Assert.assertTrue(latch.await(idleTimeout * 2, TimeUnit.MILLISECONDS));
// Be sure that the server sent a SSL close alert
TLSRecord record = proxy.readFromServer();
Assert.assertNotNull(record);
Assert.assertEquals(TLSRecord.Type.ALERT, record.getType());
// Write the request to the server, to simulate a request
// concurrent with the SSL close alert
record = proxy.readFromClient();
Assert.assertEquals(TLSRecord.Type.APPLICATION, record.getType());
proxy.flushToServer(record, 0);
// Check that we did not spin
TimeUnit.MILLISECONDS.sleep(500);
Assert.assertThat(sslHandles.get(), lessThan(20));
Assert.assertThat(sslFlushes.get(), lessThan(20));
Assert.assertThat(httpParses.get(), lessThan(50));
closeClient(client);
}
/*
@Test
public void testRequestWriteBlockedWithPipelinedRequest() throws Exception
{
final SSLSocket client = newClient();
final OutputStream clientOutput = client.getOutputStream();
SimpleProxy.AutomaticFlow automaticProxyFlow = proxy.startAutomaticFlow();
client.startHandshake();
Assert.assertTrue(automaticProxyFlow.stop(5, TimeUnit.SECONDS));
byte[] data = new byte[128 * 1024];
Arrays.fill(data, (byte)'X');
final String content = new String(data, "UTF-8");
Future<Object> request = threadPool.submit(new Callable<Object>()
{
public Object call() throws Exception
{
clientOutput.write(("" +
"POST /echo HTTP/1.1\r\n" +
"Host: localhost\r\n" +
"Content-Length: " + content.length() + "\r\n" +
"\r\n" +
content).getBytes("UTF-8"));
clientOutput.flush();
return null;
}
});
// Nine TLSRecords will be generated for the request
for (int i = 0; i < 9; ++i)
{
// Application data
TLSRecord record = proxy.readFromClient();
Assert.assertEquals(TLSRecord.Type.APPLICATION, record.getType());
proxy.flushToServer(record, 0);
}
Assert.assertNull(request.get(5, TimeUnit.SECONDS));
// We do not read the big request to cause a write blocked on the server
TimeUnit.MILLISECONDS.sleep(500);
// Now send the pipelined request
Future<Object> pipelined = threadPool.submit(new Callable<Object>()
{
public Object call() throws Exception
{
clientOutput.write(("" +
"GET /pipelined HTTP/1.1\r\n" +
"Host: localhost\r\n" +
"\r\n").getBytes("UTF-8"));
clientOutput.flush();
return null;
}
});
TLSRecord record = proxy.readFromClient();
Assert.assertEquals(TLSRecord.Type.APPLICATION, record.getType());
proxy.flushToServer(record, 0);
Assert.assertNull(pipelined.get(5, TimeUnit.SECONDS));
// Check that we did not spin
TimeUnit.MILLISECONDS.sleep(500);
Assert.assertThat(sslHandles.get(), lessThan(20));
Assert.assertThat(sslFlushes.get(), lessThan(20));
Assert.assertThat(httpParses.get(), lessThan(50));
Thread.sleep(5000);
// closeClient(client);
}
*/
private void assumeJavaVersionSupportsTLSRenegotiations()
{
// Due to a security bug, TLS renegotiations were disabled in JDK 1.6.0_19-21