jetty-9 slow progress on SSL robustness

This commit is contained in:
Greg Wilkins 2012-05-04 17:59:32 +02:00
parent ad108c42c9
commit 6e62ab9bbe
9 changed files with 205 additions and 150 deletions

View File

@ -15,20 +15,7 @@ public abstract class AbstractAsyncConnection implements AsyncConnection
private static final Logger LOG = Log.getLogger(AbstractAsyncConnection.class); private static final Logger LOG = Log.getLogger(AbstractAsyncConnection.class);
protected final AsyncEndPoint _endp; protected final AsyncEndPoint _endp;
private IOFuture.Callback _readCallback = new IOFuture.Callback() private final IOFuture.Callback _readCallback = new ReadCallback();
{
@Override
public void onReady()
{
onReadable();
}
@Override
public void onFail(Throwable cause)
{
onReadFail(cause);
}
};
public AbstractAsyncConnection(AsyncEndPoint endp) public AbstractAsyncConnection(AsyncEndPoint endp)
@ -89,4 +76,27 @@ public abstract class AbstractAsyncConnection implements AsyncConnection
{ {
return String.format("%s@%x", getClass().getSimpleName(), hashCode()); return String.format("%s@%x", getClass().getSimpleName(), hashCode());
} }
private class ReadCallback implements IOFuture.Callback
{
@Override
public void onReady()
{
onReadable();
}
@Override
public void onFail(Throwable cause)
{
onReadFail(cause);
}
@Override
public String toString()
{
return String.format("AAC$ReadCB@%x",hashCode());
}
}
} }

View File

@ -81,9 +81,8 @@ public class CompletedIOFuture implements IOFuture
@Override @Override
public String toString() public String toString()
{ {
return String.format("CIOF@%x{r=%b,c=%s}", return String.format("CIOF@%x{%s}",
hashCode(), hashCode(),
_ready, _ready?"R":_cause);
_cause);
} }
} }

View File

@ -265,11 +265,10 @@ public class DispatchedIOFuture implements IOFuture
@Override @Override
public String toString() public String toString()
{ {
return String.format("RIOF@%x{c=%b,r=%b,c=%s}", return String.format("DIOF@%x{%s,%s}",
hashCode(), hashCode(),
_complete, _complete?(_ready?"R":_cause):"-",
_ready, _callback==null?"-":_callback);
_cause);
} }
public static void rethrow(ExecutionException e) throws IOException public static void rethrow(ExecutionException e) throws IOException

View File

@ -34,7 +34,9 @@ final class RunnableIOFuture extends DispatchedIOFuture
public void run() public void run()
{ {
takeTask().run(); Runnable task=takeTask();
if (task!=null)
task.run();
} }
public boolean isDispatched() public boolean isDispatched()

View File

@ -40,6 +40,9 @@ public class SelectChannelEndPoint extends ChannelEndPoint implements AsyncEndPo
private final SelectorManager.SelectSet _selectSet; private final SelectorManager.SelectSet _selectSet;
private final SelectorManager _manager; private final SelectorManager _manager;
private final DispatchedIOFuture _readFuture = new InterestedFuture(SelectionKey.OP_READ,true,_lock);
private final DispatchedIOFuture _writeFuture = new InterestedFuture(SelectionKey.OP_WRITE,true,_lock);
private SelectionKey _key; private SelectionKey _key;
private boolean _selected; private boolean _selected;
@ -54,56 +57,7 @@ public class SelectChannelEndPoint extends ChannelEndPoint implements AsyncEndPo
private volatile boolean _idlecheck; private volatile boolean _idlecheck;
private volatile AbstractAsyncConnection _connection; private volatile AbstractAsyncConnection _connection;
private DispatchedIOFuture _readFuture = new DispatchedIOFuture(true,_lock)
{
@Override
protected void dispatch(Runnable task)
{
_manager.dispatch(task);
}
@Override
public void cancel()
{
_lock.lock();
try
{
_interestOps=_interestOps&~SelectionKey.OP_READ;
updateKey();
cancelled();
}
finally
{
_lock.unlock();
}
}
};
private ByteBuffer[] _writeBuffers; private ByteBuffer[] _writeBuffers;
private DispatchedIOFuture _writeFuture = new DispatchedIOFuture(true,_lock)
{
@Override
protected void dispatch(Runnable task)
{
_manager.dispatch(task);
}
@Override
public void cancel()
{
_lock.lock();
try
{
_interestOps=_interestOps&~SelectionKey.OP_WRITE;
updateKey();
cancelled();
}
finally
{
_lock.unlock();
}
}
};
/* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */
public SelectChannelEndPoint(SocketChannel channel, SelectSet selectSet, SelectionKey key, int maxIdleTime) public SelectChannelEndPoint(SocketChannel channel, SelectSet selectSet, SelectionKey key, int maxIdleTime)
@ -475,6 +429,7 @@ public class SelectChannelEndPoint extends ChannelEndPoint implements AsyncEndPo
@Override @Override
public void close() throws IOException public void close() throws IOException
{ {
_lock.lock();
try try
{ {
super.close(); super.close();
@ -486,6 +441,7 @@ public class SelectChannelEndPoint extends ChannelEndPoint implements AsyncEndPo
finally finally
{ {
updateKey(); updateKey();
_lock.unlock();
} }
} }
@ -518,7 +474,7 @@ public class SelectChannelEndPoint extends ChannelEndPoint implements AsyncEndPo
} }
return String.format("SCEP@%x{l(%s)<->r(%s),open=%b,ishut=%b,oshut=%b,i=%d%s}-{%s}", return String.format("SCEP@%x{l(%s)<->r(%s),open=%b,ishut=%b,oshut=%b,i=%d%s,r=%s,w=%s}-{%s}",
hashCode(), hashCode(),
getRemoteAddress(), getRemoteAddress(),
getLocalAddress(), getLocalAddress(),
@ -527,6 +483,8 @@ public class SelectChannelEndPoint extends ChannelEndPoint implements AsyncEndPo
isOutputShutdown(), isOutputShutdown(),
_interestOps, _interestOps,
keyString, keyString,
_readFuture,
_writeFuture,
getAsyncConnection()); getAsyncConnection());
} }
@ -536,6 +494,44 @@ public class SelectChannelEndPoint extends ChannelEndPoint implements AsyncEndPo
return _selectSet; return _selectSet;
} }
/* ------------------------------------------------------------ */
/* ------------------------------------------------------------ */
/* ------------------------------------------------------------ */
private class InterestedFuture extends DispatchedIOFuture
{
final int _interest;
private InterestedFuture(int interest,boolean ready, Lock lock)
{
super(ready,lock);
_interest=interest;
}
@Override
protected void dispatch(Runnable task)
{
if (!_manager.dispatch(task))
{
LOG.warn("Dispatch failed: i="+_interest);
throw new IllegalStateException();
}
}
@Override
public void cancel()
{
_lock.lock();
try
{
_interestOps=_interestOps&~_interest;
updateKey();
cancelled();
}
finally
{
_lock.unlock();
}
}
}
} }

View File

@ -45,6 +45,7 @@ import org.eclipse.jetty.util.log.Logger;
public class SslConnection extends AbstractAsyncConnection public class SslConnection extends AbstractAsyncConnection
{ {
static final Logger LOG = Log.getLogger("org.eclipse.jetty.io.ssl"); static final Logger LOG = Log.getLogger("org.eclipse.jetty.io.ssl");
private static final ByteBuffer __ZERO_BUFFER=BufferUtil.allocate(0); private static final ByteBuffer __ZERO_BUFFER=BufferUtil.allocate(0);
private static final ThreadLocal<SslBuffers> __buffers = new ThreadLocal<SslBuffers>(); private static final ThreadLocal<SslBuffers> __buffers = new ThreadLocal<SslBuffers>();
@ -238,31 +239,24 @@ public class SslConnection extends AbstractAsyncConnection
@Override @Override
public void onIdleExpired(long idleForMs) public void onIdleExpired(long idleForMs)
{ {
try System.err.println("LAST "+(System.currentTimeMillis()-_last));
{ _appConnection.onIdleExpired(idleForMs);
LOG.debug("onIdleExpired {}ms on {}",idleForMs,this);
if (_endp.isOutputShutdown())
_appEndPoint.close();
else
_appEndPoint.shutdownOutput();
}
catch (IOException e)
{
LOG.warn(e);
super.onIdleExpired(idleForMs);
}
} }
long _last;
/* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */
@Override @Override
public void onReadable() public void onReadable()
{ {
LOG.debug("onReadable {}",this);
_lock.lock(); _lock.lock();
try try
{ {
System.err.println("onReadable");
_last=System.currentTimeMillis();
LOG.debug("onReadable {}",this);
_netReadFuture=null; _netReadFuture=null;
allocateBuffers(); allocateBuffers();
@ -292,17 +286,18 @@ public class SslConnection extends AbstractAsyncConnection
finally finally
{ {
releaseBuffers(); releaseBuffers();
if (!_appReadFuture.isComplete() && _netReadFuture==null) if (!_appReadFuture.isComplete() && _netReadFuture==null && !BufferUtil.isFull(_inNet))
_netReadFuture=scheduleOnReadable(); _netReadFuture=scheduleOnReadable();
LOG.debug("!onReadable {} {}",this,_netReadFuture); LOG.debug("!onReadable {} {}",this,_netReadFuture);
_lock.unlock(); _lock.unlock();
// Run any ready callback from _appReadFuture in this thread.
_appReadFuture.run();
_appWriteFuture.run();
} }
// Run any ready callback from _appReadFuture in this thread.
if (_appReadFuture.isDispatched())
_appReadFuture.run();
} }
/* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */
@ -414,6 +409,10 @@ public class SslConnection extends AbstractAsyncConnection
} }
finally finally
{ {
// Has the net data consumed allowed us to release net backpressure?
if (BufferUtil.compact(_inNet) && !_appReadFuture.isComplete() && _netReadFuture==null)
_netReadFuture=scheduleOnReadable();
releaseBuffers(); releaseBuffers();
_lock.unlock(); _lock.unlock();
} }
@ -497,7 +496,7 @@ public class SslConnection extends AbstractAsyncConnection
int pos = BufferUtil.flipToFill(_inApp); int pos = BufferUtil.flipToFill(_inApp);
try try
{ {
result=_engine.unwrap(_inNet,_inApp); result=_engine.unwrap(_inNet,_inApp);
} }
catch(SSLException e) catch(SSLException e)
{ {
@ -516,7 +515,7 @@ public class SslConnection extends AbstractAsyncConnection
result.getStatus(), result.getStatus(),
result.getHandshakeStatus(), result.getHandshakeStatus(),
result.bytesConsumed(), result.bytesConsumed(),
result.bytesProduced()); result.bytesProduced());
switch(result.getStatus()) switch(result.getStatus())
{ {
@ -605,6 +604,9 @@ public class SslConnection extends AbstractAsyncConnection
finally finally
{ {
_lock.unlock(); _lock.unlock();
_appReadFuture.run();
_appWriteFuture.run();
} }
} }
@ -654,7 +656,10 @@ public class SslConnection extends AbstractAsyncConnection
process(null); process(null);
if (BufferUtil.hasContent(_inApp)) if (BufferUtil.hasContent(_inApp))
{
BufferUtil.append(_inApp,buffer); BufferUtil.append(_inApp,buffer);
BufferUtil.compact(_inApp);
}
} }
finally finally
{ {
@ -670,23 +675,31 @@ public class SslConnection extends AbstractAsyncConnection
@Override @Override
public int flush(ByteBuffer... buffers) throws IOException public int flush(ByteBuffer... buffers) throws IOException
{ {
int len=0; _lock.lock();
bufloop: for (ByteBuffer b : buffers) try
{ {
while (b.hasRemaining()) int len=0;
bufloop: for (ByteBuffer b : buffers)
{ {
int l = b.remaining(); while (b.hasRemaining())
if (!process(b)) {
break bufloop; int l = b.remaining();
l=l-b.remaining(); if (!process(b))
break bufloop;
if (l>0) l=l-b.remaining();
len+=l;
else if (l>0)
break bufloop; len+=l;
else
break bufloop;
}
} }
return len;
}
finally
{
_lock.unlock();
} }
return len;
} }
@Override @Override
@ -726,11 +739,12 @@ public class SslConnection extends AbstractAsyncConnection
int i = inbound == null? -1 : inbound.remaining(); int i = inbound == null? -1 : inbound.remaining();
int o = outbound == null ? -1 : outbound.remaining(); int o = outbound == null ? -1 : outbound.remaining();
int u = unwrap == null ? -1 : unwrap.remaining(); int u = unwrap == null ? -1 : unwrap.remaining();
return String.format("SSL %s %s i/o/u=%d/%d/%d ep.ishut=%b oshut=%b {%s}", return String.format("SSL%s[%s,i/o/u=%d/%d/%d,ep.ishut=%b,oshut=%b,r=%s,w=%s}-{%s}",
super.toString(), super.toString(),
_engine.getHandshakeStatus(), _engine.getHandshakeStatus(),
i, o, u, i, o, u,
_endp.isInputShutdown(), _oshut, _endp.isInputShutdown(), _oshut,
_appReadFuture,_appWriteFuture,
_appConnection); _appConnection);
} }
@ -806,6 +820,8 @@ public class SslConnection extends AbstractAsyncConnection
finally finally
{ {
_lock.unlock(); _lock.unlock();
_appReadFuture.run();
_appWriteFuture.run();
} }
} }
@ -831,9 +847,8 @@ public class SslConnection extends AbstractAsyncConnection
finally finally
{ {
_lock.unlock(); _lock.unlock();
_appReadFuture.run();
if (_appWriteFuture.isDispatched()) _appWriteFuture.run();
_appWriteFuture.run();
} }
} }
} }

View File

@ -194,6 +194,6 @@ public class SelectChannelEndPointSslTest extends SelectChannelEndPointTest
@Override @Override
public void testStress() throws Exception public void testStress() throws Exception
{ {
// super.testStress(); super.testStress();
} }
} }

View File

@ -7,6 +7,7 @@ import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import java.io.BufferedInputStream; import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
@ -21,6 +22,7 @@ import java.nio.channels.SocketChannel;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import org.eclipse.jetty.io.AsyncConnection; import org.eclipse.jetty.io.AsyncConnection;
import org.eclipse.jetty.io.AbstractAsyncConnection; import org.eclipse.jetty.io.AbstractAsyncConnection;
@ -119,6 +121,7 @@ public class SelectChannelEndPointTest
{ {
ByteBuffer _in = BufferUtil.allocate(32*1024); ByteBuffer _in = BufferUtil.allocate(32*1024);
ByteBuffer _out = BufferUtil.allocate(32*1024); ByteBuffer _out = BufferUtil.allocate(32*1024);
long _last=-1;
public TestConnection(AsyncEndPoint endp) public TestConnection(AsyncEndPoint endp)
{ {
@ -126,10 +129,12 @@ public class SelectChannelEndPointTest
} }
@Override @Override
public void onReadable() public synchronized void onReadable()
{ {
System.err.println("APP onReadable");
try try
{ {
_last=System.currentTimeMillis();
_endp.setCheckForIdle(false); _endp.setCheckForIdle(false);
boolean progress=true; boolean progress=true;
while(progress) while(progress)
@ -137,18 +142,17 @@ public class SelectChannelEndPointTest
progress=false; progress=false;
// Fill the input buffer with everything available // Fill the input buffer with everything available
if (!BufferUtil.isFull(_in)) if (BufferUtil.isFull(_in))
{ throw new IllegalStateException("FULL "+BufferUtil.toDetailString(_in));
int filled=_endp.fill(_in); int filled=_endp.fill(_in);
if (filled>0) if (filled>0)
progress=true; progress=true;
}
// If the tests wants to block, then block // If the tests wants to block, then block
while (_blockAt>0 && _endp.isOpen() && _in.remaining()<_blockAt) while (_blockAt>0 && _endp.isOpen() && _in.remaining()<_blockAt)
{ {
_endp.read().block(); _endp.read().block();
int filled=_endp.fill(_in); filled=_endp.fill(_in);
progress|=filled>0; progress|=filled>0;
} }
@ -162,21 +166,15 @@ public class SelectChannelEndPointTest
ByteBuffer out=_out.duplicate(); ByteBuffer out=_out.duplicate();
BufferUtil.clear(_out); BufferUtil.clear(_out);
for (int i=0;i<_writeCount;i++) for (int i=0;i<_writeCount;i++)
{
_endp.write(out.asReadOnlyBuffer()).block(); _endp.write(out.asReadOnlyBuffer()).block();
}
progress=true; progress=true;
} }
// are we done? // are we done?
if (BufferUtil.isEmpty(_out) && _endp.isInputShutdown()) if (_endp.isInputShutdown())
_endp.shutdownOutput(); _endp.shutdownOutput();
} }
} }
catch(ClosedChannelException e)
{
// System.err.println(e);
}
catch(ExecutionException e) catch(ExecutionException e)
{ {
// Timeout does not close, so echo exception then shutdown // Timeout does not close, so echo exception then shutdown
@ -190,10 +188,6 @@ public class SelectChannelEndPointTest
e2.printStackTrace(); e2.printStackTrace();
} }
} }
catch(InterruptedException e)
{
// System.err.println(e);
}
catch(Exception e) catch(Exception e)
{ {
e.printStackTrace(); e.printStackTrace();
@ -208,10 +202,28 @@ public class SelectChannelEndPointTest
} }
} }
@Override
public void onIdleExpired(long idleForMs)
{
System.err.println("IDLE "+idleForMs);
System.err.println("last "+(System.currentTimeMillis()-_last));
System.err.println("ENDP "+_endp);
System.err.println("tran "+_endp.getTransport());
System.err.println();
super.onIdleExpired(idleForMs);
}
@Override @Override
public void onClose() public void onClose()
{ {
} }
@Override
public String toString()
{
return String.format("%s{}",
super.toString());
}
} }
@ -365,7 +377,6 @@ public class SelectChannelEndPointTest
catch(SocketTimeoutException e) catch(SocketTimeoutException e)
{ {
int elapsed = Long.valueOf(System.currentTimeMillis() - start).intValue(); int elapsed = Long.valueOf(System.currentTimeMillis() - start).intValue();
// System.err.println("blocked for " + elapsed+ "ms");
Assert.assertThat("Expected timeout", elapsed, greaterThanOrEqualTo(3*specifiedTimeout/4)); Assert.assertThat("Expected timeout", elapsed, greaterThanOrEqualTo(3*specifiedTimeout/4));
} }
@ -491,7 +502,7 @@ public class SelectChannelEndPointTest
public void testStress() throws Exception public void testStress() throws Exception
{ {
Socket client = newClient(); Socket client = newClient();
client.setSoTimeout(30000); client.setSoTimeout(60000);
SocketChannel server = _connector.accept(); SocketChannel server = _connector.accept();
server.configureBlocking(false); server.configureBlocking(false);
@ -501,17 +512,25 @@ public class SelectChannelEndPointTest
final byte[] bytes="HelloWorld-".getBytes(StringUtil.__UTF8_CHARSET); final byte[] bytes="HelloWorld-".getBytes(StringUtil.__UTF8_CHARSET);
byte[] count="0\n".getBytes(StringUtil.__UTF8_CHARSET); byte[] count="0\n".getBytes(StringUtil.__UTF8_CHARSET);
BufferedOutputStream out = new BufferedOutputStream(client.getOutputStream());
final CountDownLatch latch = new CountDownLatch(writes); final CountDownLatch latch = new CountDownLatch(writes);
final InputStream in = new BufferedInputStream(client.getInputStream()); final InputStream in = new BufferedInputStream(client.getInputStream());
final long start = System.currentTimeMillis(); final long start = System.currentTimeMillis();
client.getOutputStream().write(bytes); out.write(bytes);
client.getOutputStream().write(count); out.write(count);
client.getOutputStream().flush(); out.flush();
while (_lastEndp==null)
Thread.sleep(10);
_lastEndp.setMaxIdleTime(5000);
new Thread() new Thread()
{ {
public void run() public void run()
{ {
Thread.currentThread().setPriority(MAX_PRIORITY);
long last=-1;
int count=-1;
try try
{ {
while (latch.getCount()>0) while (latch.getCount()>0)
@ -524,36 +543,42 @@ public class SelectChannelEndPointTest
assertEquals(0xff&b0,b); assertEquals(0xff&b0,b);
} }
count=0;
int b=in.read(); int b=in.read();
while(b>0 && b!='\n') while(b>0 && b!='\n')
{
count=count*10+(b-'0');
b=in.read(); b=in.read();
}
last=System.currentTimeMillis();
latch.countDown(); latch.countDown();
} }
} }
catch(Throwable e) catch(Throwable e)
{ {
long now = System.currentTimeMillis();
System.err.println("count="+count);
System.err.println("latch="+latch.getCount()); System.err.println("latch="+latch.getCount());
System.err.println("time="+(System.currentTimeMillis()-start)); System.err.println("time="+(now-start));
System.err.println("last="+(now-last));
System.err.println("endp="+_lastEndp);
e.printStackTrace(); e.printStackTrace();
} }
} }
}.start(); }.start();
PrintStream print = new PrintStream(client.getOutputStream());
// Write client to server // Write client to server
for (int i=1;i<writes;i++) for (int i=1;i<writes;i++)
{ {
print.write(bytes); out.write(bytes);
print.print(i); out.write(Integer.toString(i).getBytes(StringUtil.__ISO_8859_1_CHARSET));
print.print('\n'); out.write('\n');
if (i%100==0) if (i%100==0)
print.flush(); out.flush();
Thread.yield(); Thread.yield();
} }
client.getOutputStream().flush(); out.flush();
assertTrue(latch.await(100,TimeUnit.SECONDS)); assertTrue(latch.await(100,TimeUnit.SECONDS));
} }

View File

@ -17,6 +17,7 @@ import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.RandomAccessFile; import java.io.RandomAccessFile;
import java.nio.Buffer;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer; import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel; import java.nio.channels.FileChannel;
@ -240,10 +241,13 @@ public class BufferUtil
/* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */
/** Compact the buffer /** Compact the buffer
* @param buffer * @param buffer
* @return true if the compact made a full buffer have space
*/ */
public static void compact(ByteBuffer buffer) public static boolean compact(ByteBuffer buffer)
{ {
boolean full=buffer.limit()==buffer.capacity();
buffer.compact().flip(); buffer.compact().flip();
return full && buffer.limit()<buffer.capacity();
} }
/* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */
@ -263,6 +267,8 @@ public class BufferUtil
{ {
to.put(from); to.put(from);
put=remaining; put=remaining;
from.position(0);
from.limit(0);
} }
else if (from.hasArray()) else if (from.hasArray())
{ {
@ -659,7 +665,10 @@ public class BufferUtil
StringBuilder buf = new StringBuilder(); StringBuilder buf = new StringBuilder();
buf.append(buffer.getClass().getSimpleName()); buf.append(buffer.getClass().getSimpleName());
buf.append("@"); buf.append("@");
buf.append(Integer.toHexString(buffer.hashCode())); if (buffer.hasArray())
buf.append(Integer.toHexString(buffer.array().hashCode()));
else
buf.append("?");
buf.append("[p="); buf.append("[p=");
buf.append(buffer.position()); buf.append(buffer.position());
buf.append(",l="); buf.append(",l=");