jetty-9 - Throwing EofException when trying to flush after output shutdown.

When the output is shutdown, field _cannotAcceptMoreAppDataToFlush is set to true,
indicating that no more flush() calls are accepted.
However, a further call to flush() was reading _cannotAcceptMoreAppDataToFlush and
just returning false, leaving writes in pending state but without chance to be completed.
Now, if we detect _cannotAcceptMoreAppDataToFlush==true in flush(), we check
whether the output was shutdown; if it is the case, we throw an EofException to signal
to the application that it cannot write because the connection is already closed.
This commit is contained in:
Simone Bordet 2012-09-14 15:20:10 +02:00
parent 655a313fdf
commit 8c9f097666
1 changed files with 33 additions and 16 deletions

View File

@ -21,6 +21,7 @@ package org.eclipse.jetty.io.ssl;
import java.io.IOException;
import java.net.SocketException;
import java.nio.ByteBuffer;
import java.nio.channels.ClosedChannelException;
import java.util.Arrays;
import java.util.concurrent.Executor;
import javax.net.ssl.SSLEngine;
@ -79,6 +80,7 @@ import org.eclipse.jetty.util.log.Logger;
public class SslConnection extends AbstractConnection
{
private static final Logger LOG = Log.getLogger(SslConnection.class);
private static final boolean DEBUG = LOG.isDebugEnabled(); // Easy for the compiler to remove the code if DEBUG==false
private static final ByteBuffer __FILL_CALLED_FLUSH= BufferUtil.allocate(0);
private static final ByteBuffer __FLUSH_CALLED_FILL= BufferUtil.allocate(0);
private final ByteBufferPool _bufferPool;
@ -189,6 +191,7 @@ public class SslConnection extends AbstractConnection
// to do the fill and/or flush again and these calls will do the actually
// filling.
if (DEBUG)
LOG.debug("onFillable enter {}", getEndPoint());
// wake up whoever is doing the fill or the flush so they can
@ -205,6 +208,7 @@ public class SslConnection extends AbstractConnection
}
}
if (DEBUG)
LOG.debug("onFillable exit {}", getEndPoint());
}
@ -268,6 +272,7 @@ public class SslConnection extends AbstractConnection
// data. In either case the appropriate callback is passed on.
synchronized (DecryptedEndPoint.this)
{
if (DEBUG)
LOG.debug("write.complete {}", SslConnection.this.getEndPoint());
releaseEncryptedOutputBuffer();
@ -292,6 +297,7 @@ public class SslConnection extends AbstractConnection
// data. In either case the appropriate callback is passed on.
synchronized (DecryptedEndPoint.this)
{
if (DEBUG)
LOG.debug("{} write.failed", SslConnection.this, x);
if (_encryptedOutput != null)
BufferUtil.clear(_encryptedOutput);
@ -345,6 +351,7 @@ public class SslConnection extends AbstractConnection
// if neither then we should just try the flush again.
synchronized (DecryptedEndPoint.this)
{
if (DEBUG)
LOG.debug("onIncompleteFlush {}", getEndPoint());
// If we have pending output data,
if (BufferUtil.hasContent(_encryptedOutput))
@ -447,6 +454,7 @@ public class SslConnection extends AbstractConnection
@Override
public synchronized int fill(ByteBuffer buffer) throws IOException
{
if (DEBUG)
LOG.debug("{} fill enter", SslConnection.this);
try
{
@ -474,6 +482,7 @@ public class SslConnection extends AbstractConnection
{
// Let's try reading some encrypted data... even if we have some already.
int net_filled = getEndPoint().fill(_encryptedInput);
if (DEBUG)
LOG.debug("{} filled {} encrypted bytes", SslConnection.this, net_filled);
if (net_filled > 0)
_underFlown = false;
@ -483,6 +492,7 @@ public class SslConnection extends AbstractConnection
int pos = BufferUtil.flipToFill(app_in);
SSLEngineResult unwrapResult = _sslEngine.unwrap(_encryptedInput, app_in);
BufferUtil.flipToFlush(app_in, pos);
if (DEBUG)
LOG.debug("{} unwrap {}", SslConnection.this, unwrapResult);
// and deal with the results
@ -614,7 +624,6 @@ public class SslConnection extends AbstractConnection
catch (SSLException e)
{
getEndPoint().close();
LOG.debug(e);
throw new EofException(e);
}
catch (Exception e)
@ -641,6 +650,7 @@ public class SslConnection extends AbstractConnection
_bufferPool.release(_decryptedInput);
_decryptedInput = null;
}
if (DEBUG)
LOG.debug("{} fill exit", SslConnection.this);
}
}
@ -655,12 +665,17 @@ public class SslConnection extends AbstractConnection
// it is the applications responsibility to call flush again - either in a busy loop
// or better yet by using EndPoint#write to do the flushing.
if (DEBUG)
LOG.debug("{} flush enter {}", SslConnection.this, Arrays.toString(appOuts));
int consumed=0;
try
{
if (_cannotAcceptMoreAppDataToFlush)
{
if (_sslEngine.isOutboundDone())
throw new EofException(new ClosedChannelException());
return false;
}
// We will need a network buffer
if (_encryptedOutput == null)
@ -673,6 +688,7 @@ public class SslConnection extends AbstractConnection
BufferUtil.compact(_encryptedOutput);
int pos = BufferUtil.flipToFill(_encryptedOutput);
SSLEngineResult wrapResult = _sslEngine.wrap(appOuts, _encryptedOutput);
if (DEBUG)
LOG.debug("{} wrap {}", SslConnection.this, wrapResult);
BufferUtil.flipToFlush(_encryptedOutput, pos);
if (wrapResult.bytesConsumed()>0)
@ -711,7 +727,7 @@ public class SslConnection extends AbstractConnection
throw new IllegalStateException();
default:
if (LOG.isDebugEnabled())
if (DEBUG)
LOG.debug("{} {} {}", this, wrapResult.getStatus(), BufferUtil.toDetailString(_encryptedOutput));
// if we have net bytes, let's try to flush them
@ -759,6 +775,7 @@ public class SslConnection extends AbstractConnection
}
finally
{
if (DEBUG)
LOG.debug("{} flush exit, consumed {}", SslConnection.this, consumed);
releaseEncryptedOutputBuffer();
}