364921 - FIN WAIT sockets.

Interim commit, as things are not working exactly right yet.
This commit is contained in:
Simone Bordet 2013-04-12 23:17:24 +02:00
parent 9fd155d36e
commit 70e6655ec5
4 changed files with 81 additions and 181 deletions

View File

@ -356,13 +356,8 @@ public class SslBytesClientTest extends SslBytesTest
Assert.assertEquals(TLSRecord.Type.HANDSHAKE, record.getType());
proxy.flushToClient(record);
record = proxy.readFromClient();
Assert.assertEquals(TLSRecord.Type.ALERT, record.getType());
proxy.flushToServer(record);
record = proxy.readFromClient();
Assert.assertNull(record);
proxy.flushToServer(record);
server.close();
}

View File

@ -72,7 +72,6 @@ import org.junit.After;
import org.junit.Assert;
import org.junit.Assume;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
public class SslBytesServerTest extends SslBytesTest
@ -87,6 +86,7 @@ public class SslBytesServerTest extends SslBytesTest
private SslContextFactory sslContextFactory;
private SSLContext sslContext;
private SimpleProxy proxy;
private Runnable idleHook;
@Before
public void init() throws Exception
@ -119,6 +119,15 @@ public class SslBytesServerTest extends SslBytesTest
}
};
}
@Override
protected boolean onReadTimeout()
{
final Runnable idleHook = SslBytesServerTest.this.idleHook;
if (idleHook != null)
idleHook.run();
return super.onReadTimeout();
}
}, connector, endPoint);
}
};
@ -165,7 +174,6 @@ public class SslBytesServerTest extends SslBytesTest
}
};
connector.setIdleTimeout(idleTimeout);
// connector.setPort(5870);
connector.setPort(0);
server.addConnector(connector);
@ -209,7 +217,7 @@ public class SslBytesServerTest extends SslBytesTest
proxy = new SimpleProxy(threadPool, "localhost", serverPort);
proxy.start();
logger.debug(":{} <==> :{}", proxy.getPort(), serverPort);
logger.info("proxy:{} <==> server:{}", proxy.getPort(), serverPort);
}
@After
@ -377,13 +385,9 @@ public class SslBytesServerTest extends SslBytesTest
Assert.assertNull(String.valueOf(record), record);
proxy.flushToServer(record);
// Close Alert
record = proxy.readFromServer();
proxy.flushToClient(record);
// Socket close
record = proxy.readFromServer();
Assert.assertNull(String.valueOf(record), record);
proxy.flushToClient(record);
}
@Test
@ -669,13 +673,9 @@ public class SslBytesServerTest extends SslBytesTest
Assert.assertNull(String.valueOf(record), record);
proxy.flushToServer(record);
// Close Alert
record = proxy.readFromServer();
proxy.flushToClient(record);
// Socket close
record = proxy.readFromServer();
Assert.assertNull(String.valueOf(record), record);
proxy.flushToClient(record);
}
@Test
@ -728,22 +728,15 @@ public class SslBytesServerTest extends SslBytesTest
Assert.assertEquals(TLSRecord.Type.APPLICATION, record.getType());
proxy.flushToClient(record);
// Close Alert
// Socket close
record = proxy.readFromServer();
Assert.assertNotNull(record);
Assert.assertEquals(TLSRecord.Type.ALERT, record.getType());
// We can't forward to the client, its socket is already closed
Assert.assertNull(record);
// Check that we did not spin
TimeUnit.MILLISECONDS.sleep(500);
Assert.assertThat(sslFills.get(), Matchers.lessThan(20));
Assert.assertThat(sslFlushes.get(), Matchers.lessThan(20));
Assert.assertThat(httpParses.get(), Matchers.lessThan(20));
// Socket close
record = proxy.readFromServer();
Assert.assertNull(String.valueOf(record), record);
proxy.flushToClient(record);
}
@Test
@ -803,11 +796,9 @@ public class SslBytesServerTest extends SslBytesTest
Assert.assertEquals(TLSRecord.Type.APPLICATION, record.getType());
proxy.flushToClient(record);
// Close Alert
// Socket close
record = proxy.readFromServer();
Assert.assertNotNull(record);
Assert.assertEquals(TLSRecord.Type.ALERT, record.getType());
// We can't forward to the client, its socket is already closed
Assert.assertNull(String.valueOf(record), record);
// Check that we did not spin
TimeUnit.MILLISECONDS.sleep(500);
@ -819,11 +810,6 @@ public class SslBytesServerTest extends SslBytesTest
record = proxy.readFromClient();
Assert.assertNull(String.valueOf(record), record);
proxy.flushToServer(record);
// Socket close
record = proxy.readFromServer();
Assert.assertNull(String.valueOf(record), record);
proxy.flushToClient(record);
}
@Test
@ -864,12 +850,9 @@ public class SslBytesServerTest extends SslBytesTest
// Close the raw socket, this generates a truncation attack
proxy.flushToServer(null);
// Expect alert + raw close from server
record = proxy.readFromServer();
Assert.assertEquals(TLSRecord.Type.ALERT, record.getType());
// Expect raw close from server
record = proxy.readFromServer();
Assert.assertNull(String.valueOf(record), record);
proxy.flushToClient(record);
// Check that we did not spin
TimeUnit.MILLISECONDS.sleep(500);
@ -917,12 +900,9 @@ public class SslBytesServerTest extends SslBytesTest
Assert.assertEquals(TLSRecord.Type.APPLICATION, record.getType());
proxy.flushToClient(record);
// Expect alert + raw close from server
record = proxy.readFromServer();
Assert.assertEquals(TLSRecord.Type.ALERT, record.getType());
// Expect raw close from server
record = proxy.readFromServer();
Assert.assertNull(String.valueOf(record), record);
proxy.flushToClient(record);
// Check that we did not spin
TimeUnit.MILLISECONDS.sleep(500);
@ -1099,6 +1079,7 @@ public class SslBytesServerTest extends SslBytesTest
System.arraycopy(closeBytes, 0, bytes, dataBytes.length, closeBytes.length / 2);
proxy.flushToServer(100, bytes);
// Send the other half of the close alert bytes
bytes = new byte[closeBytes.length - closeBytes.length / 2];
System.arraycopy(closeBytes, closeBytes.length / 2, bytes, 0, bytes.length);
proxy.flushToServer(100, bytes);
@ -1113,27 +1094,15 @@ public class SslBytesServerTest extends SslBytesTest
Assert.assertEquals(TLSRecord.Type.APPLICATION, record.getType());
proxy.flushToClient(record);
// Close Alert
// Socket close
record = proxy.readFromServer();
Assert.assertNotNull(record);
Assert.assertEquals(TLSRecord.Type.ALERT, record.getType());
// We can't forward to the client, its socket is already closed
Assert.assertNull(record);
// Check that we did not spin
TimeUnit.MILLISECONDS.sleep(500);
Assert.assertThat(sslFills.get(), Matchers.lessThan(20));
Assert.assertThat(sslFlushes.get(), Matchers.lessThan(20));
Assert.assertThat(httpParses.get(), Matchers.lessThan(20));
// Socket close
record = proxy.readFromClient();
Assert.assertNull(String.valueOf(record), record);
proxy.flushToServer(record);
// Socket close
record = proxy.readFromServer();
Assert.assertNull(String.valueOf(record), record);
proxy.flushToClient(record);
}
@Test
@ -1749,13 +1718,37 @@ public class SslBytesServerTest extends SslBytesTest
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()
{
if (latch.getCount()==0)
return;
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
x.printStackTrace();
}
}
};
SimpleProxy.AutomaticFlow automaticProxyFlow = proxy.startAutomaticFlow();
client.startHandshake();
Assert.assertTrue(automaticProxyFlow.stop(5, TimeUnit.SECONDS));
@ -1779,86 +1772,13 @@ public class SslBytesServerTest extends SslBytesTest
Assert.assertThat(sslFlushes.get(), Matchers.lessThan(20));
Assert.assertThat(httpParses.get(), Matchers.lessThan(50));
completeClose(client);
record = proxy.readFromServer();
Assert.assertNull(record);
TimeUnit.MILLISECONDS.sleep(200);
//System.err.println(((Dumpable)server.getConnectors()[0]).dump());
Assert.assertThat(((Dumpable)server.getConnectors()[0]).dump(), Matchers.not(Matchers.containsString("SCEP@")));
}
/*
@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(sslFills.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
@ -1893,31 +1813,8 @@ public class SslBytesServerTest extends SslBytesTest
Assert.assertNull(String.valueOf(record), record);
proxy.flushToServer(record);
// Close Alert
record = proxy.readFromServer();
proxy.flushToClient(record);
// Socket close
record = proxy.readFromServer();
Assert.assertNull(String.valueOf(record), record);
proxy.flushToClient(record);
}
private void completeClose(SSLSocket client) throws Exception
{
client.close();
// Close Alert
TLSRecord record = proxy.readFromClient();
proxy.flushToServer(record);
// Socket close
record = proxy.readFromClient();
Assert.assertNull(String.valueOf(record), record);
proxy.flushToServer(record);
// Close Alert
record = proxy.readFromServer();
proxy.flushToClient(record);
}
}

View File

@ -34,12 +34,17 @@ import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import org.eclipse.jetty.toolchain.test.TestTracker;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.junit.Assert;
import org.junit.Rule;
public abstract class SslBytesTest
{
@Rule
public TestTracker tracker = new TestTracker();
protected final Logger logger = Log.getLogger(getClass());
public static class TLSRecord
@ -115,8 +120,7 @@ public abstract class SslBytesTest
public void start() throws Exception
{
serverSocket = new ServerSocket(47009);
// serverSocket = new ServerSocket(0);
serverSocket = new ServerSocket(0);
Thread acceptor = new Thread(this);
acceptor.start();
server = new Socket(serverHost, serverPort);

View File

@ -22,7 +22,6 @@ import java.io.IOException;
import java.net.SocketException;
import java.nio.ByteBuffer;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.ReadPendingException;
import java.util.Arrays;
import java.util.concurrent.Executor;
import javax.net.ssl.SSLEngine;
@ -426,7 +425,7 @@ public class SslConnection extends AbstractConnection
return true;
}
}
else
else if (!isOutputShutdown())
{
// Normal readable callback
// Get called back on onfillable when then is more data to fill
@ -613,8 +612,21 @@ public class SslConnection extends AbstractConnection
// maybe we will fill some more on a retry
continue;
}
// we need to wait for more net data
return 0;
else
{
if (isOutputShutdown())
{
// We have sent the SSL Close Alert, and we read 0 bytes:
// it's a peer that it is not sending the FIN, so we just
// return -1 as if we did, so the connection will be closed.
return -1;
}
else
{
// we need to wait for more net data
return 0;
}
}
case FINISHED:
throw new IllegalStateException();
@ -735,9 +747,11 @@ public class SslConnection extends AbstractConnection
if (BufferUtil.hasContent(_encryptedOutput))
return false;
}
// otherwise we have written, and the caller will close the underlying connection
getEndPoint().shutdownOutput();
else
{
getEndPoint().shutdownOutput();
}
return allConsumed;
case BUFFER_UNDERFLOW:
@ -834,11 +848,15 @@ public class SslConnection extends AbstractConnection
@Override
public void shutdownOutput()
{
if (isInputShutdown())
boolean ishut = isInputShutdown();
if (DEBUG)
LOG.debug("{} shutdownOutput: oshut={}, ishut={}", SslConnection.this, isOutputShutdown(), ishut);
if (ishut)
{
// Aggressively close because inbound close handshake already processed
// and most impls are expecting a FIN rather than a reply. When a reply is sent, many impls will do
// a RST.
// Aggressively close, since inbound close alert has already been processed
// and the TLS specification allows to close the connection directly, which
// is what most other implementations expect: a FIN rather than a TLS close
// reply. If a TLS close reply is sent, most implementation send a RST.
getEndPoint().close();
}
else
@ -847,20 +865,6 @@ public class SslConnection extends AbstractConnection
{
_sslEngine.closeOutbound();
flush(BufferUtil.EMPTY_BUFFER);
fillInterested(new Callback()
{
@Override
public void succeeded()
{
getEndPoint().close();
}
@Override
public void failed(Throwable x)
{
getEndPoint().close();
}
});
}
catch (Exception e)
{