Merge branch 'jetty-9' of ssh://git.eclipse.org/gitroot/jetty/org.eclipse.jetty.project into jetty-9

This commit is contained in:
Joakim Erdfelt 2012-08-01 10:47:05 -07:00
commit 4b59767bd2
3 changed files with 258 additions and 170 deletions

View File

@ -20,6 +20,7 @@ import java.util.concurrent.Executor;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLEngineResult;
import javax.net.ssl.SSLEngineResult.HandshakeStatus;
import javax.net.ssl.SSLEngineResult.Status;
import javax.net.ssl.SSLException;
import org.eclipse.jetty.io.AbstractAsyncConnection;
@ -39,31 +40,54 @@ import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
/**
* An AsyncConnection that acts as an interceptor between and EndPoint and another
* Connection, that implements TLS encryption using an {@link SSLEngine}.
* <p/>
* The connector uses an {@link EndPoint} (like {@link SelectChannelEndPoint}) as
* it's source/sink of encrypted data. It then provides {@link #getSslEndPoint()} to
* An AsyncConnection that acts as an intercepter between an AsyncEndPoint providing SSL encrypted data
* and another consumer of an AsyncEndPoint (typically an {@link AsyncConnection} like HttpConnection) that
* wants unencrypted data.
* <p>
* The connector uses an {@link AsyncEndPoint} (typically {@link SelectChannelEndPoint}) as
* it's source/sink of encrypted data. It then provides an endpoint via {@link #getSslEndPoint()} to
* expose a source/sink of unencrypted data to another connection (eg HttpConnection).
* <p>
* The design of this class is based on a clear separation between the passive methods, which do not block nor schedule any
* asynchronous callbacks, and active methods that do schedule asynchronous callbacks.
* <p>
* The passive methods are {@link DecryptedEndPoint#fill(ByteBuffer)} and {@link DecryptedEndPoint#flush(ByteBuffer...)}. They make best
* effort attempts to progress the connection using only calls to the encrypted {@link AsyncEndPoint#fill(ByteBuffer)} and {@link AsyncEndPoint#flush(ByteBuffer...)}
* methods. They will never block nor schedule any readInterest or write callbacks. If a fill/flush cannot progress either because
* of network congestion or waiting for an SSL handshake message, then the fill/flush will simply return with zero bytes filled/flushed.
* Specifically, if a flush cannot proceed because it needs to receive a handshake message, then the flush will attempt to fill bytes from the
* encrypted endpoint, but if insufficient bytes are read it will NOT call {@link AsyncEndPoint#fillInterested(Object, Callback)}.
* <p>
* It is only the active methods : {@link DecryptedEndPoint#fillInterested(Object, Callback)} and
* {@link DecryptedEndPoint#write(Object, Callback, ByteBuffer...)} that may schedule callbacks by calling the encrypted
* {@link AsyncEndPoint#fillInterested(Object, Callback)} and {@link AsyncEndPoint#write(Object, Callback, ByteBuffer...)}
* methods. For normal data handling, the decrypted fillInterest method will result in an encrypted fillInterest and a decrypted
* write will result in an encrypted write. However, due to SSL handshaking requirements, it is also possible for a decrypted fill
* to call the encrypted write and for the decrypted flush to call the encrypted fillInterested methods.
* <p>
* MOST IMPORTANTLY, the encrypted callbacks from the active methods (#onFillable() and WriteFlusher#completeWrite()) do no filling or flushing
* themselves. Instead they simple make the callbacks to the decrypted callbacks, so that the passive encyrpted fill/flush will
* be called again and make another best effort attempt to progress the connection.
*
*/
public class SslConnection extends AbstractAsyncConnection
{
private static final Logger LOG = Log.getLogger(SslConnection.class);
private final ByteBufferPool _bufferPool;
private final SSLEngine _sslEngine;
private final SslEndPoint _appEndPoint;
private ByteBuffer _appIn;
private ByteBuffer _netIn;
private ByteBuffer _netOut;
private final boolean _netDirect = false;
private final boolean _appDirect = false;
private final DecryptedEndPoint _decryptedEndPoint;
private ByteBuffer _decryptedInput;
private ByteBuffer _encryptedInput;
private ByteBuffer _encryptedOutput;
private final boolean _encryptedDirectBuffers = false;
private final boolean _decryptedDirectBuffers = false;
public SslConnection(ByteBufferPool byteBufferPool, Executor executor, AsyncEndPoint endPoint, SSLEngine sslEngine)
{
super(endPoint, executor, true);
this._bufferPool = byteBufferPool;
this._sslEngine = sslEngine;
this._appEndPoint = new SslEndPoint();
this._decryptedEndPoint = new DecryptedEndPoint();
}
public SSLEngine getSSLEngine()
@ -73,7 +97,7 @@ public class SslConnection extends AbstractAsyncConnection
public AsyncEndPoint getSslEndPoint()
{
return _appEndPoint;
return _decryptedEndPoint;
}
@Override
@ -87,7 +111,7 @@ public class SslConnection extends AbstractAsyncConnection
_sslEngine.beginHandshake();
if (_sslEngine.getUseClientMode())
_appEndPoint.write(null, new Callback.Empty<>(), BufferUtil.EMPTY_BUFFER);
_decryptedEndPoint.write(null, new Callback.Empty<>(), BufferUtil.EMPTY_BUFFER);
}
catch (SSLException x)
{
@ -100,18 +124,27 @@ public class SslConnection extends AbstractAsyncConnection
@Override
public void onFillable()
{
// onFillable means that there are encrypted bytes ready to be filled.
// however we do not fill them here on this callback, but instead wakeup
// the decrypted readInterest and/or writeFlusher so that they will attempt
// to do the fill and/or flush again and these calls will do the actually
// filling.
LOG.debug("{} onReadable", this);
// wake up whoever is doing the fill or the flush so they can
// do all the filling, unwrapping ,wrapping and flushing
if (_appEndPoint._readInterest.isInterested())
_appEndPoint._readInterest.readable();
// If we are handshaking, then wake up any waiting write as well as it may have been blocked on the read
if (_appEndPoint._writeFlusher.isWriting() && _appEndPoint._flushUnwrap)
synchronized(_decryptedEndPoint)
{
_appEndPoint._flushUnwrap = false;
_appEndPoint._writeFlusher.completeWrite();
// wake up whoever is doing the fill or the flush so they can
// do all the filling, unwrapping ,wrapping and flushing
if (_decryptedEndPoint._readInterest.isInterested())
_decryptedEndPoint._readInterest.readable();
// If we are handshaking, then wake up any waiting write as well as it may have been blocked on the read
if ( _decryptedEndPoint._flushRequiresFillToProgress)
{
_decryptedEndPoint._flushRequiresFillToProgress = false;
_decryptedEndPoint._writeFlusher.completeWrite();
}
}
}
@ -119,17 +152,25 @@ public class SslConnection extends AbstractAsyncConnection
@Override
public void onFillInterestedFailed(Throwable cause)
{
// this means that the fill interest in encrypted bytes has failed.
// However we do not handle that here on this callback, but instead wakeup
// the decrypted readInterest and/or writeFlusher so that they will attempt
// to do the fill and/or flush again and these calls will do the actually
// handle the cause.
super.onFillInterestedFailed(cause);
if (_appEndPoint._readInterest.isInterested())
_appEndPoint._readInterest.failed(cause);
if (_appEndPoint._writeFlusher.isWriting() && _appEndPoint._flushUnwrap)
synchronized(_decryptedEndPoint)
{
_appEndPoint._flushUnwrap = false;
_appEndPoint._writeFlusher.failed(cause);
}
if (_decryptedEndPoint._readInterest.isInterested())
_decryptedEndPoint._readInterest.failed(cause);
if (_decryptedEndPoint._flushRequiresFillToProgress)
{
_decryptedEndPoint._flushRequiresFillToProgress = false;
_decryptedEndPoint._writeFlusher.failed(cause);
}
}
}
/* ------------------------------------------------------------ */
@ -139,18 +180,18 @@ public class SslConnection extends AbstractAsyncConnection
return String.format("SslConnection@%x{%s,%s%s}",
hashCode(),
_sslEngine.getHandshakeStatus(),
_appEndPoint._readInterest.isInterested() ? "R" : "",
_appEndPoint._writeFlusher.isWriting() ? "W" : "");
_decryptedEndPoint._readInterest.isInterested() ? "R" : "",
_decryptedEndPoint._writeFlusher.isWriting() ? "W" : "");
}
/* ------------------------------------------------------------ */
public class SslEndPoint extends AbstractEndPoint implements AsyncEndPoint
public class DecryptedEndPoint extends AbstractEndPoint implements AsyncEndPoint
{
private AsyncConnection _connection;
private boolean _fillWrap;
private boolean _flushUnwrap;
private boolean _netWriting;
private boolean _underflown;
private boolean _fillRequiresFlushToProgress;
private boolean _flushRequiresFillToProgress;
private boolean _cannotAcceptMoreAppDataToFlush;
private boolean _needToFillMoreDataToProgress;
private boolean _ishut = false;
@Override
@ -169,16 +210,20 @@ public class SslConnection extends AbstractAsyncConnection
@Override
public void completed(Void context)
{
synchronized (SslEndPoint.this)
// This means that a write of data has completed. Writes are done
// only if there is an active writeflusher or a read needed to write
// data. In either case the appropriate callback is passed on.
synchronized (DecryptedEndPoint.this)
{
LOG.debug("{} write.complete {}", SslConnection.this, _netWriting ? (_fillWrap ? "FW" : "F") : (_fillWrap ? "W" : ""));
LOG.debug("{} write.complete {}", SslConnection.this, _cannotAcceptMoreAppDataToFlush ? (_fillRequiresFlushToProgress ? "FW" : "F") : (_fillRequiresFlushToProgress ? "W" : ""));
releaseNetOut();
_netWriting = false;
if (_fillWrap)
_cannotAcceptMoreAppDataToFlush = false;
if (_fillRequiresFlushToProgress)
{
_fillWrap = false;
_fillRequiresFlushToProgress = false;
_readInterest.readable();
}
@ -190,16 +235,21 @@ public class SslConnection extends AbstractAsyncConnection
@Override
public void failed(Void context, Throwable x)
{
synchronized (SslEndPoint.this)
// This means that a write of data has failed. Writes are done
// only if there is an active writeflusher or a read needed to write
// data. In either case the appropriate callback is passed on.
synchronized (DecryptedEndPoint.this)
{
LOG.debug("{} write.failed", SslConnection.this, x);
if (_netOut != null)
BufferUtil.clear(_netOut);
if (_encryptedOutput != null)
BufferUtil.clear(_encryptedOutput);
releaseNetOut();
_netWriting = false;
if (_fillWrap)
_cannotAcceptMoreAppDataToFlush = false;
if (_fillRequiresFlushToProgress)
{
_fillWrap = false;
_fillRequiresFlushToProgress = false;
_readInterest.failed(x);
}
@ -216,39 +266,54 @@ public class SslConnection extends AbstractAsyncConnection
@Override
protected boolean needsFill() throws IOException
{
synchronized (SslEndPoint.this)
// This means that the decrypted data consumer has called the fillInterested
// method on the DecryptedEndPoint, so we have to work out if there is
// decrypted data to be filled or what callbacks to setup to be told when there
// might be more encrypted data available to attempt another call to fill
synchronized (DecryptedEndPoint.this)
{
// Do we already have some app data
if (BufferUtil.hasContent(_appIn))
// Do we already have some app data, then app can fill now so return true
if (BufferUtil.hasContent(_decryptedInput))
return true;
// If we are not underflown and have net data
if (!_underflown && BufferUtil.hasContent(_netIn))
return true;
// So we are not read ready
// Are we actually write blocked?
if (_fillWrap)
// If we have no encrypted data to decrypt OR we have some, but it is not enough
if (BufferUtil.isEmpty(_encryptedInput) || _needToFillMoreDataToProgress)
{
// we must be blocked trying to write before we can read
// If we have written the net data
if (BufferUtil.isEmpty(_netOut))
{
// pretend we are readable so the wrap is done by next readable callback
_fillWrap = false;
return true;
}
// We are not ready to read data
// otherwise write the net data
_netWriting = true;
getEndPoint().write(null, _writeCallback, _netOut);
// Are we actually write blocked?
if (_fillRequiresFlushToProgress)
{
// we must be blocked trying to write before we can read
// Do we have data to write
if (BufferUtil.hasContent(_encryptedOutput))
{
// write it
_cannotAcceptMoreAppDataToFlush = true;
getEndPoint().write(null, _writeCallback, _encryptedOutput);
}
else
{
// we have already written the net data
// pretend we are readable so the wrap is done by next readable callback
_fillRequiresFlushToProgress = false;
return true;
}
}
else
// Normal readable callback
// Get called back on onfillable when then is more data to fill
SslConnection.this.fillInterested();
return false;
}
else
// Normal readable callback
SslConnection.this.fillInterested();
return false;
{
// We are ready to read data
return true;
}
}
}
};
@ -258,16 +323,19 @@ public class SslConnection extends AbstractAsyncConnection
@Override
protected void onIncompleteFlushed()
{
synchronized (SslEndPoint.this)
// This means that the decripted endpoint write method was called and not
// all data could be wrapped. So either we need to write some encrypted data,
// OR if we are handshaking we need to read some encrypted data OR
// if neither than we should just try the flush again.
synchronized (DecryptedEndPoint.this)
{
// If we have pending output data,
if (BufferUtil.hasContent(_netOut))
if (BufferUtil.hasContent(_encryptedOutput))
{
// write it
_netWriting = true;
getEndPoint().write(null, _writeCallback, _netOut);
_cannotAcceptMoreAppDataToFlush = true;
getEndPoint().write(null, _writeCallback, _encryptedOutput);
}
// TODO test this with _flushUnwrap
else if (_sslEngine.getHandshakeStatus() == HandshakeStatus.NEED_UNWRAP)
// we are actually read blocked in order to write
SslConnection.this.fillInterested();
@ -278,7 +346,7 @@ public class SslConnection extends AbstractAsyncConnection
}
};
public SslEndPoint()
public DecryptedEndPoint()
{
super(getEndPoint().getLocalAddress(), getEndPoint().getRemoteAddress());
}
@ -307,37 +375,37 @@ public class SslConnection extends AbstractAsyncConnection
try
{
// Do we already have some decrypted data?
if (BufferUtil.hasContent(_appIn))
return BufferUtil.append(_appIn, buffer);
if (BufferUtil.hasContent(_decryptedInput))
return BufferUtil.append(_decryptedInput, buffer);
// We will need a network buffer
if (_netIn == null)
_netIn = _bufferPool.acquire(_sslEngine.getSession().getPacketBufferSize(), _netDirect);
if (_encryptedInput == null)
_encryptedInput = _bufferPool.acquire(_sslEngine.getSession().getPacketBufferSize(), _encryptedDirectBuffers);
else
BufferUtil.compact(_netIn);
BufferUtil.compact(_encryptedInput);
// We also need an app buffer, but can use the passed buffer if it is big enough
ByteBuffer app_in;
if (BufferUtil.space(buffer) > _sslEngine.getSession().getApplicationBufferSize())
app_in = buffer;
else if (_appIn == null)
app_in = _appIn = _bufferPool.acquire(_sslEngine.getSession().getApplicationBufferSize(), _appDirect);
else if (_decryptedInput == null)
app_in = _decryptedInput = _bufferPool.acquire(_sslEngine.getSession().getApplicationBufferSize(), _decryptedDirectBuffers);
else
app_in = _appIn;
app_in = _decryptedInput;
// loop filling and unwrapping until we have something
while (true)
{
// Let's try reading some encrypted data... even if we have some already.
int net_filled = getEndPoint().fill(_netIn);
int net_filled = getEndPoint().fill(_encryptedInput);
LOG.debug("{} filled {} encrypted bytes", SslConnection.this, net_filled);
if (net_filled > 0)
_underflown = false;
_needToFillMoreDataToProgress = false;
// Let's try the SSL thang even if we have no net data because in that
// case we want to fall through to the handshake handling
int pos = BufferUtil.flipToFill(app_in);
SSLEngineResult unwrapResult = _sslEngine.unwrap(_netIn, app_in);
SSLEngineResult unwrapResult = _sslEngine.unwrap(_encryptedInput, app_in);
LOG.debug("{} unwrap {}", SslConnection.this, unwrapResult);
BufferUtil.flipToFlush(app_in, pos);
@ -348,10 +416,11 @@ public class SslConnection extends AbstractAsyncConnection
throw new IllegalStateException();
case CLOSED:
// Dang! we have to care about the handshake state
// Dang! we have to care about the handshake state specially for close
switch (_sslEngine.getHandshakeStatus())
{
case NOT_HANDSHAKING:
// We were not handshaking, so just tell the app we are closed
return -1;
case NEED_TASK:
@ -360,40 +429,47 @@ public class SslConnection extends AbstractAsyncConnection
continue;
case NEED_WRAP:
// we need to send some handshake data
if (!_flushUnwrap)
// we need to send some handshake data (probably to send a close handshake).
if (_flushRequiresFillToProgress)
return -1; // we were called from flush, so it can deal with sending the close handshake
// We need to call flush to cause the wrap to happen
_fillRequiresFlushToProgress = true;
try
{
_fillWrap = true;
try
{
flush(BufferUtil.EMPTY_BUFFER);
}
catch(IOException e)
// flushing an empty buffer will invoke the wrap mechanisms
flush(BufferUtil.EMPTY_BUFFER);
// If encrypted output is all written, we can proceed with close
if (BufferUtil.isEmpty(_encryptedOutput))
{
_fillRequiresFlushToProgress = false;
return -1;
}
if (BufferUtil.hasContent(_netOut))
return 0;
_fillWrap = false;
// Otherwise return as if a normal fill and let a subsequent call
// return -1 to the caller.
return unwrapResult.bytesProduced();
}
catch(IOException e)
{
LOG.debug(e);
// The flush failed, oh well nothing more to do than tell the app
// that the connection is closed.
return -1;
}
return -1;
default:
throw new IllegalStateException();
}
case BUFFER_UNDERFLOW:
_underflown = true;
//$FALL-THROUGH$ to deal with handshaking stuff
throw new IllegalStateException();
default:
// if we produced bytes, we don't care about the handshake state
if (unwrapResult.getStatus()==Status.BUFFER_UNDERFLOW)
_needToFillMoreDataToProgress=true;
// if we produced bytes, we don't care about the handshake state for now and it can be dealt with on another call to fill or flush
if (unwrapResult.bytesProduced() > 0)
{
if (app_in == buffer)
return unwrapResult.bytesProduced();
return BufferUtil.append(_appIn, buffer);
return BufferUtil.append(_decryptedInput, buffer);
}
// Dang! we have to care about the handshake state
@ -412,14 +488,17 @@ public class SslConnection extends AbstractAsyncConnection
case NEED_WRAP:
// we need to send some handshake data
if (_flushUnwrap)
if (_flushRequiresFillToProgress)
return 0;
_fillWrap = true;
_fillRequiresFlushToProgress = true;
flush(BufferUtil.EMPTY_BUFFER);
if (BufferUtil.hasContent(_netOut))
return 0;
_fillWrap = false;
continue;
if (BufferUtil.isEmpty(_encryptedOutput))
{
// the flush completed so continue
_fillRequiresFlushToProgress = false;
continue;
}
return 0;
case NEED_UNWRAP:
// if we just filled some net data
@ -450,15 +529,15 @@ public class SslConnection extends AbstractAsyncConnection
}
finally
{
if (_netIn != null && !_netIn.hasRemaining())
if (_encryptedInput != null && !_encryptedInput.hasRemaining())
{
_bufferPool.release(_netIn);
_netIn = null;
_bufferPool.release(_encryptedInput);
_encryptedInput = null;
}
if (_appIn != null && !_appIn.hasRemaining())
if (_decryptedInput != null && !_decryptedInput.hasRemaining())
{
_bufferPool.release(_appIn);
_appIn = null;
_bufferPool.release(_decryptedInput);
_decryptedInput = null;
}
LOG.debug("{} fill exit", SslConnection.this);
}
@ -477,77 +556,86 @@ public class SslConnection extends AbstractAsyncConnection
LOG.debug("{} flush enter {}", SslConnection.this, Arrays.toString(appOuts));
try
{
if (_netWriting)
if (_cannotAcceptMoreAppDataToFlush)
return 0;
// We will need a network buffer
if (_netOut == null)
_netOut = _bufferPool.acquire(_sslEngine.getSession().getPacketBufferSize() * 2, _netDirect);
if (_encryptedOutput == null)
_encryptedOutput = _bufferPool.acquire(_sslEngine.getSession().getPacketBufferSize() * 2, _encryptedDirectBuffers);
int consumed=0;
while (true)
{
// do the funky SSL thang!
BufferUtil.compact(_netOut);
int pos = BufferUtil.flipToFill(_netOut);
SSLEngineResult wrapResult = _sslEngine.wrap(appOuts, _netOut);
// We call sslEngine.wrap to try to take bytes from appOut buffers and encrypt them into the _netOut buffer
BufferUtil.compact(_encryptedOutput);
int pos = BufferUtil.flipToFill(_encryptedOutput);
SSLEngineResult wrapResult = _sslEngine.wrap(appOuts, _encryptedOutput);
LOG.debug("{} wrap {}", SslConnection.this, wrapResult);
BufferUtil.flipToFlush(_netOut, pos);
BufferUtil.flipToFlush(_encryptedOutput, pos);
consumed+=wrapResult.bytesConsumed();
// and deal with the results
// and deal with the results returned from the sslEngineWrap
switch (wrapResult.getStatus())
{
case CLOSED:
if (BufferUtil.hasContent(_netOut))
// The SSL engine has close, but there may be close handshake that needs to be written
if (BufferUtil.hasContent(_encryptedOutput))
{
_netWriting = true;
getEndPoint().flush(_netOut);
if (BufferUtil.hasContent(_netOut))
return 0;
_cannotAcceptMoreAppDataToFlush = true;
getEndPoint().flush(_encryptedOutput);
// If we failed to flush the close handshake then we will just pretend that
// the write has progressed normally and let a subsequent call to flush (or WriteFlusher#onIncompleteFlushed)
// to finish writing the close handshake. The caller will find out about the close on a subsequent flush or fill.
if (BufferUtil.hasContent(_encryptedOutput))
return consumed;
}
if (_fillWrap)
return 0;
// If we we flushing because of a fill needing to wrap, return normally and it will handle the closed state.
if (_fillRequiresFlushToProgress)
return consumed;
// otherwise it is an exception to write to a closed endpoint
throw new EofException();
case BUFFER_UNDERFLOW:
throw new IllegalStateException();
case BUFFER_OVERFLOW:
if (LOG.isDebugEnabled())
LOG.debug("{} OVERFLOW {}", this, BufferUtil.toDetailString(_netOut));
//$FALL-THROUGH$
default:
if (LOG.isDebugEnabled())
LOG.debug("{} {} {}", this, wrapResult.getStatus(), BufferUtil.toDetailString(_encryptedOutput));
// if we have net bytes, let's try to flush them
if (BufferUtil.hasContent(_netOut))
{
getEndPoint().flush(_netOut);
return wrapResult.bytesConsumed();
}
if (BufferUtil.hasContent(_encryptedOutput))
getEndPoint().flush(_encryptedOutput);
// Dang! we have to deal with handshake state
// But we also might have more to do for the handshaking state.
switch (_sslEngine.getHandshakeStatus())
{
case NOT_HANDSHAKING:
// we just didn't write anything. Strange?
return 0;
// Return with the number of bytes consumed (which may be 0)
return consumed;
case NEED_TASK:
// run the task
// run the task and continue
_sslEngine.getDelegatedTask().run();
continue;
case NEED_WRAP:
// Hey we just wrapped!
// Hey we just wrapped! Oh well who knows what the sslEngine is thinking, so continue and we will wrap again
continue;
case NEED_UNWRAP:
// Were we were not called from fill and not reading anyway
if (!_fillWrap && !_readInterest.isInterested())
// Ah we need to fill some data so we can write.
// So if we were not called from fill and the app is not reading anyway
if (!_fillRequiresFlushToProgress && !_readInterest.isInterested())
{
_flushUnwrap = true;
// Tell the onFillable method that there might be a write to complete
// TODO move this to the writeFlusher?
_flushRequiresFillToProgress = true;
fill(BufferUtil.EMPTY_BUFFER);
}
return 0;
return consumed;
case FINISHED:
throw new IllegalStateException();
@ -570,10 +658,10 @@ public class SslConnection extends AbstractAsyncConnection
private void releaseNetOut()
{
if (_netOut != null && !_netOut.hasRemaining())
if (_encryptedOutput != null && !_encryptedOutput.hasRemaining())
{
_bufferPool.release(_netOut);
_netOut = null;
_bufferPool.release(_encryptedOutput);
_encryptedOutput = null;
if (_sslEngine.isOutboundDone())
getEndPoint().shutdownOutput();
}
@ -639,7 +727,7 @@ public class SslConnection extends AbstractAsyncConnection
@Override
public String toString()
{
return String.format("%s{%s%s%s}", super.toString(), _readInterest.isInterested() ? "R" : "", _writeFlusher.isWriting() ? "W" : "", _netWriting ? "w" : "");
return String.format("%s{%s%s%s}", super.toString(), _readInterest.isInterested() ? "R" : "", _writeFlusher.isWriting() ? "W" : "", _cannotAcceptMoreAppDataToFlush ? "w" : "");
}
}

View File

@ -88,7 +88,7 @@ public class SslSelectChannelConnector extends SelectChannelConnector implements
request.setScheme(HttpScheme.HTTPS.asString());
super.customize(request);
SslConnection.SslEndPoint ssl_endp = (SslConnection.SslEndPoint)request.getHttpChannel().getEndPoint();
SslConnection.DecryptedEndPoint ssl_endp = (SslConnection.DecryptedEndPoint)request.getHttpChannel().getEndPoint();
SslConnection sslConnection = ssl_endp.getSslConnection();
SSLEngine sslEngine=sslConnection.getSSLEngine();
SslCertificates.customize(sslEngine,request);

View File

@ -149,8 +149,8 @@ public abstract class ConnectorTimeoutTest extends HttpServerTestFixture
// Get the server side endpoint
EndPoint endp = endpoint.exchange(null,10,TimeUnit.SECONDS);
if (endp instanceof SslConnection.SslEndPoint)
endp=((SslConnection.SslEndPoint)endp).getAsyncConnection().getEndPoint();
if (endp instanceof SslConnection.DecryptedEndPoint)
endp=((SslConnection.DecryptedEndPoint)endp).getAsyncConnection().getEndPoint();
// read the response
String result=IO.toString(is);
@ -222,8 +222,8 @@ public abstract class ConnectorTimeoutTest extends HttpServerTestFixture
// Get the server side endpoint
EndPoint endp = endpoint.exchange(null,10,TimeUnit.SECONDS);
if (endp instanceof SslConnection.SslEndPoint)
endp=((SslConnection.SslEndPoint)endp).getAsyncConnection().getEndPoint();
if (endp instanceof SslConnection.DecryptedEndPoint)
endp=((SslConnection.DecryptedEndPoint)endp).getAsyncConnection().getEndPoint();
// read the response
String result=IO.toString(is);