diff --git a/jetty-documentation/src/main/asciidoc/quick-start/introduction/what-version.adoc b/jetty-documentation/src/main/asciidoc/quick-start/introduction/what-version.adoc index a4657f313cb..25c2782a90c 100644 --- a/jetty-documentation/src/main/asciidoc/quick-start/introduction/what-version.adoc +++ b/jetty-documentation/src/main/asciidoc/quick-start/introduction/what-version.adoc @@ -17,19 +17,19 @@ [[what-jetty-version]] === What Version Do I Use? -Jetty 9 is the most recent version of Jetty and has a great many improvements over previous versions. -One improvement is this documentation which focuses on Jetty 9. +Jetty 9 is the most recent version of Jetty and has a great many improvements over previous versions. +This documentation which focuses on Jetty 9. While many people continue to use older versions of Jetty, we generally recommend using Jetty 9 as it represents the version of Jetty that we will actively maintain and improve over the next few years. .Jetty Versions [width="100%",cols="12%,9%,15%,6%,21%,10%,6%,21%",options="header",] |======================================================================= |Version |Year |Home |JVM |Protocols |Servlet |JSP |Status +|9.4 |2016 |Eclipse |1.8 |HTTP/1.1 (RFC 7230), HTTP/2 (RFC 7540), WebSocket (RFC 6455, JSR 356), FastCGI |3.1 |2.3 |Stable |9.3 |2015 |Eclipse |1.8 |HTTP/1.1 (RFC 7230), HTTP/2 (RFC 7540), WebSocket (RFC 6455, JSR 356), FastCGI |3.1 |2.3 |Stable |9.2 |2014 |Eclipse |1.7 |HTTP/1.1 RFC2616, javax.websocket, SPDY v3 |3.1 |2.3 |Stable -|8.2 |2009- |Eclipse/Codehaus |1.7 |HTTP/1.1 RFC2616, WebSocket RFC 6455, SPDY v3 |3.0 |2.2 |Venerable -|8.1 |2009- |Eclipse/Codehaus |1.6 |HTTP/1.1 RFC2616, WebSocket RFC 6455, SPDY v3 |3.0 |2.2 |Venerable -|7 |2008- |Eclipse/Codehaus |1.5 |HTTP/1.1 RFC2616, WebSocket RFC 6455, SPDY v3 |2.5 |2.1 |Venerable +|8 |2009-2014 |Eclipse/Codehaus |1.6 |HTTP/1.1 RFC2616, WebSocket RFC 6455, SPDY v3 |3.0 |2.2 |Deprecated +|7 |2008-2014 |Eclipse/Codehaus |1.5 |HTTP/1.1 RFC2616, WebSocket RFC 6455, SPDY v3 |2.5 |2.1 |Deprecated |6 |2006-2010 |Codehaus |1.4-1.5 |HTTP/1.1 RFC2616 |2.5 |2.0 |Deprecated |5 |2003-2009 |Sourceforge |1.2-1.5 |HTTP/1.1 RFC2616 |2.4 |2.0 |Deprecated |4 |2001-2006 |Sourceforge |1.2, J2ME |HTTP/1.1 RFC2616 |2.3 |1.2 |Ancient diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslConnection.java b/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslConnection.java index 135ffc67eaf..2edccc06924 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslConnection.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslConnection.java @@ -92,8 +92,7 @@ public class SslConnection extends AbstractConnection private final boolean _decryptedDirectBuffers = false; private boolean _renegotiationAllowed; private boolean _closedOutbound; - - + private abstract class RunnableTask implements Runnable, Invocable { private final String _operation; @@ -109,7 +108,7 @@ public class SslConnection extends AbstractConnection return String.format("SSL:%s:%s:%s",SslConnection.this,_operation,getInvocationType()); } } - + private final Runnable _runCompleteWrite = new RunnableTask("runCompleteWrite") { @Override @@ -117,14 +116,14 @@ public class SslConnection extends AbstractConnection { _decryptedEndPoint.getWriteFlusher().completeWrite(); } - + @Override public InvocationType getInvocationType() { return getDecryptedEndPoint().getWriteFlusher().getCallbackInvocationType(); } }; - + private final Runnable _runFillable = new RunnableTask("runFillable") { @Override @@ -132,15 +131,15 @@ public class SslConnection extends AbstractConnection { _decryptedEndPoint.getFillInterest().fillable(); } - + @Override public InvocationType getInvocationType() { return getDecryptedEndPoint().getFillInterest().getCallbackInvocationType(); } }; - - Callback _sslReadCallback = new Callback() + + private final Callback _sslReadCallback = new Callback() { @Override public void succeeded() @@ -309,12 +308,12 @@ public class SslConnection extends AbstractConnection int di=b==null?-1:b.remaining(); return String.format("%s@%x{%s,eio=%d/%d,di=%d}=>%s", - getClass().getSimpleName(), + getClass().getSimpleName(), hashCode(), _sslEngine.getHandshakeStatus(), ei,eo,di, ((AbstractConnection)_decryptedEndPoint.getConnection()).toConnectionString()); - } + } public class DecryptedEndPoint extends AbstractEndPoint { @@ -353,35 +352,31 @@ public class SslConnection extends AbstractConnection // 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. - boolean fail_filler = false; + boolean fail_filler; synchronized (DecryptedEndPoint.this) { if (LOG.isDebugEnabled()) - LOG.debug("{} write.failed", SslConnection.this, x); + LOG.debug("{} write failed", SslConnection.this, x); + BufferUtil.clear(_encryptedOutput); releaseEncryptedOutputBuffer(); _cannotAcceptMoreAppDataToFlush = false; - + fail_filler = _fillRequiresFlushToProgress; if (_fillRequiresFlushToProgress) - { _fillRequiresFlushToProgress = false; - fail_filler = true; - } } - final boolean filler_failed=fail_filler; - failedCallback(new Callback() { @Override public void failed(Throwable x) { - if (filler_failed) + if (fail_filler) getFillInterest().onFail(x); getWriteFlusher().onFail(x); } - },x); + }, x); } @Override @@ -389,7 +384,7 @@ public class SslConnection extends AbstractConnection { return getWriteFlusher().getCallbackInvocationType(); } - + @Override public String toString() { @@ -473,7 +468,7 @@ public class SslConnection extends AbstractConnection else if (_sslEngine.getHandshakeStatus() == HandshakeStatus.NEED_UNWRAP) { // check if we are actually read blocked in order to write - _flushRequiresFillToProgress = true; + _flushRequiresFillToProgress = true; need_fill_interest = !SslConnection.this.isFillInterested(); } else @@ -488,7 +483,7 @@ public class SslConnection extends AbstractConnection } if (write) - getEndPoint().write(_writeCallback, _encryptedOutput); + getEndPoint().write(_writeCallback, _encryptedOutput); else if (need_fill_interest) ensureFillInterested(); else if (try_again) @@ -507,7 +502,6 @@ public class SslConnection extends AbstractConnection getExecutor().execute(_runCompleteWrite); } } - } @Override @@ -557,7 +551,7 @@ public class SslConnection extends AbstractConnection getEndPoint().write(_writeCallback, _encryptedOutput); else if (fillable) getExecutor().execute(_runFillable); - else + else ensureFillInterested(); } @@ -579,100 +573,191 @@ public class SslConnection extends AbstractConnection } @Override - public synchronized int fill(ByteBuffer buffer) throws IOException + public int fill(ByteBuffer buffer) throws IOException { try { - // Do we already have some decrypted data? - if (BufferUtil.hasContent(_decryptedInput)) - return BufferUtil.append(buffer,_decryptedInput); - - // We will need a network buffer - if (_encryptedInput == null) - _encryptedInput = _bufferPool.acquire(_sslEngine.getSession().getPacketBufferSize(), _encryptedDirectBuffers); - else - 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 (_decryptedInput == null) - app_in = _decryptedInput = _bufferPool.acquire(_sslEngine.getSession().getApplicationBufferSize(), _decryptedDirectBuffers); - else - app_in = _decryptedInput; - - // loop filling and unwrapping until we have something - while (true) + synchronized (this) { - // Let's try reading some encrypted data... even if we have some already. - int net_filled = getEndPoint().fill(_encryptedInput); - - decryption: while (true) + try { - // Let's unwrap 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; - try - { - unwrapResult = _sslEngine.unwrap(_encryptedInput, app_in); - } - finally - { - BufferUtil.flipToFlush(app_in, pos); - } - if (LOG.isDebugEnabled()) - { - LOG.debug("{} net={} unwrap {}", SslConnection.this, net_filled, unwrapResult.toString().replace('\n',' ')); - LOG.debug("{} filled {}",SslConnection.this,BufferUtil.toHexSummary(buffer)); - } + // Do we already have some decrypted data? + if (BufferUtil.hasContent(_decryptedInput)) + return BufferUtil.append(buffer,_decryptedInput); - HandshakeStatus handshakeStatus = _sslEngine.getHandshakeStatus(); - HandshakeStatus unwrapHandshakeStatus = unwrapResult.getHandshakeStatus(); - Status unwrapResultStatus = unwrapResult.getStatus(); + // We will need a network buffer + if (_encryptedInput == null) + _encryptedInput = _bufferPool.acquire(_sslEngine.getSession().getPacketBufferSize(), _encryptedDirectBuffers); + else + BufferUtil.compact(_encryptedInput); - // Extra check on unwrapResultStatus == OK with zero bytes consumed - // or produced is due to an SSL client on Android (see bug #454773). - _underFlown = unwrapResultStatus == Status.BUFFER_UNDERFLOW || - unwrapResultStatus == Status.OK && unwrapResult.bytesConsumed() == 0 && unwrapResult.bytesProduced() == 0; + // 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 (_decryptedInput == null) + app_in = _decryptedInput = _bufferPool.acquire(_sslEngine.getSession().getApplicationBufferSize(), _decryptedDirectBuffers); + else + app_in = _decryptedInput; - if (_underFlown) + // loop filling and unwrapping until we have something + while (true) { - if (net_filled < 0) - closeInbound(); - if (net_filled <= 0) - return net_filled; - } + // Let's try reading some encrypted data... even if we have some already. + int net_filled = getEndPoint().fill(_encryptedInput); - switch (unwrapResultStatus) - { - case CLOSED: + decryption: while (true) { - switch (handshakeStatus) + // Let's unwrap 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; + try { - case NOT_HANDSHAKING: + unwrapResult = _sslEngine.unwrap(_encryptedInput, app_in); + } + finally + { + BufferUtil.flipToFlush(app_in, pos); + } + if (LOG.isDebugEnabled()) + { + LOG.debug("{} net={} unwrap {}", SslConnection.this, net_filled, unwrapResult.toString().replace('\n',' ')); + LOG.debug("{} filled {}",SslConnection.this,BufferUtil.toHexSummary(buffer)); + } + + HandshakeStatus handshakeStatus = _sslEngine.getHandshakeStatus(); + HandshakeStatus unwrapHandshakeStatus = unwrapResult.getHandshakeStatus(); + Status unwrapResultStatus = unwrapResult.getStatus(); + + // Extra check on unwrapResultStatus == OK with zero bytes consumed + // or produced is due to an SSL client on Android (see bug #454773). + _underFlown = unwrapResultStatus == Status.BUFFER_UNDERFLOW || + unwrapResultStatus == Status.OK && unwrapResult.bytesConsumed() == 0 && unwrapResult.bytesProduced() == 0; + + if (_underFlown) + { + if (net_filled < 0) + closeInbound(); + if (net_filled <= 0) + return net_filled; + } + + switch (unwrapResultStatus) + { + case CLOSED: { - // We were not handshaking, so just tell the app we are closed - return -1; + switch (handshakeStatus) + { + case NOT_HANDSHAKING: + { + // We were not handshaking, so just tell the app we are closed + return -1; + } + case NEED_TASK: + { + _sslEngine.getDelegatedTask().run(); + continue; + } + case NEED_WRAP: + { + // We need to send some handshake data (probably the close handshake). + // We return -1 so that the application can drive the close by flushing + // or shutting down the output. + return -1; + } + case NEED_UNWRAP: + { + // We expected to read more, but we got closed. + // Return -1 to indicate to the application to drive the close. + return -1; + } + default: + { + throw new IllegalStateException(); + } + } } - case NEED_TASK: + case BUFFER_UNDERFLOW: + case OK: { - _sslEngine.getDelegatedTask().run(); - continue; - } - case NEED_WRAP: - { - // We need to send some handshake data (probably the close handshake). - // We return -1 so that the application can drive the close by flushing - // or shutting down the output. - return -1; - } - case NEED_UNWRAP: - { - // We expected to read more, but we got closed. - // Return -1 to indicate to the application to drive the close. - return -1; + if (unwrapHandshakeStatus == HandshakeStatus.FINISHED && !_handshaken) + { + _handshaken = true; + if (LOG.isDebugEnabled()) + LOG.debug("{} {} handshake succeeded {}/{}", SslConnection.this, + _sslEngine.getUseClientMode() ? "client" : "resumed server", + _sslEngine.getSession().getProtocol(),_sslEngine.getSession().getCipherSuite()); + notifyHandshakeSucceeded(_sslEngine); + } + + // Check whether renegotiation is allowed + if (_handshaken && handshakeStatus != HandshakeStatus.NOT_HANDSHAKING && !isRenegotiationAllowed()) + { + if (LOG.isDebugEnabled()) + LOG.debug("{} renegotiation denied", SslConnection.this); + closeInbound(); + return -1; + } + + // If bytes were produced, don't bother with the handshake status; + // pass the decrypted data to the application, which will perform + // another call to fill() or flush(). + if (unwrapResult.bytesProduced() > 0) + { + if (app_in == buffer) + return unwrapResult.bytesProduced(); + return BufferUtil.append(buffer,_decryptedInput); + } + + switch (handshakeStatus) + { + case NOT_HANDSHAKING: + { + if (_underFlown) + break decryption; + continue; + } + case NEED_TASK: + { + _sslEngine.getDelegatedTask().run(); + continue; + } + case NEED_WRAP: + { + // If we are called from flush() + // return to let it do the wrapping. + if (_flushRequiresFillToProgress) + return 0; + + _fillRequiresFlushToProgress = true; + flush(BufferUtil.EMPTY_BUFFER); + if (BufferUtil.isEmpty(_encryptedOutput)) + { + // The flush wrote all the encrypted bytes so continue to fill. + _fillRequiresFlushToProgress = false; + if (_underFlown) + break decryption; + continue; + } + else + { + // The flush did not complete, return from fill() + // and let the write completion mechanism to kick in. + return 0; + } + } + case NEED_UNWRAP: + { + if (_underFlown) + break decryption; + continue; + } + default: + { + throw new IllegalStateException(); + } + } } default: { @@ -680,136 +765,49 @@ public class SslConnection extends AbstractConnection } } } - case BUFFER_UNDERFLOW: - case OK: - { - if (unwrapHandshakeStatus == HandshakeStatus.FINISHED && !_handshaken) - { - _handshaken = true; - if (LOG.isDebugEnabled()) - LOG.debug("{} {} handshake succeeded {}/{}", SslConnection.this, - _sslEngine.getUseClientMode() ? "client" : "resumed server", - _sslEngine.getSession().getProtocol(),_sslEngine.getSession().getCipherSuite()); - notifyHandshakeSucceeded(_sslEngine); - } - - // Check whether renegotiation is allowed - if (_handshaken && handshakeStatus != HandshakeStatus.NOT_HANDSHAKING && !isRenegotiationAllowed()) - { - if (LOG.isDebugEnabled()) - LOG.debug("{} renegotiation denied", SslConnection.this); - closeInbound(); - return -1; - } - - // If bytes were produced, don't bother with the handshake status; - // pass the decrypted data to the application, which will perform - // another call to fill() or flush(). - if (unwrapResult.bytesProduced() > 0) - { - if (app_in == buffer) - return unwrapResult.bytesProduced(); - return BufferUtil.append(buffer,_decryptedInput); - } - - switch (handshakeStatus) - { - case NOT_HANDSHAKING: - { - if (_underFlown) - break decryption; - continue; - } - case NEED_TASK: - { - _sslEngine.getDelegatedTask().run(); - continue; - } - case NEED_WRAP: - { - // If we are called from flush() - // return to let it do the wrapping. - if (_flushRequiresFillToProgress) - return 0; - - _fillRequiresFlushToProgress = true; - flush(BufferUtil.EMPTY_BUFFER); - if (BufferUtil.isEmpty(_encryptedOutput)) - { - // The flush wrote all the encrypted bytes so continue to fill. - _fillRequiresFlushToProgress = false; - if (_underFlown) - break decryption; - continue; - } - else - { - // The flush did not complete, return from fill() - // and let the write completion mechanism to kick in. - return 0; - } - } - case NEED_UNWRAP: - { - if (_underFlown) - break decryption; - continue; - } - default: - { - throw new IllegalStateException(); - } - } - } - default: - { - throw new IllegalStateException(); - } + } + } + catch (SSLHandshakeException x) + { + notifyHandshakeFailed(_sslEngine, x); + throw x; + } + catch (SSLException x) + { + if (!_handshaken) + { + x = (SSLException)new SSLHandshakeException(x.getMessage()).initCause(x); + notifyHandshakeFailed(_sslEngine, x); + } + throw x; + } + finally + { + // If we are handshaking, then wake up any waiting write as well as it may have been blocked on the read + if (_flushRequiresFillToProgress) + { + _flushRequiresFillToProgress = false; + getExecutor().execute(_runCompleteWrite); + } + + if (_encryptedInput != null && !_encryptedInput.hasRemaining()) + { + _bufferPool.release(_encryptedInput); + _encryptedInput = null; + } + if (_decryptedInput != null && !_decryptedInput.hasRemaining()) + { + _bufferPool.release(_decryptedInput); + _decryptedInput = null; } } } } - catch (SSLHandshakeException x) - { - notifyHandshakeFailed(_sslEngine, x); - close(x); - throw x; - } - catch (SSLException x) - { - if (!_handshaken) - { - x = (SSLException)new SSLHandshakeException(x.getMessage()).initCause(x); - notifyHandshakeFailed(_sslEngine, x); - } - close(x); - throw x; - } catch (Throwable x) { close(x); throw x; } - finally - { - // If we are handshaking, then wake up any waiting write as well as it may have been blocked on the read - if (_flushRequiresFillToProgress) - { - _flushRequiresFillToProgress = false; - getExecutor().execute(_runCompleteWrite); - } - - if (_encryptedInput != null && !_encryptedInput.hasRemaining()) - { - _bufferPool.release(_encryptedInput); - _encryptedInput = null; - } - if (_decryptedInput != null && !_decryptedInput.hasRemaining()) - { - _bufferPool.release(_decryptedInput); - _decryptedInput = null; - } - } } private void closeInbound() @@ -825,7 +823,7 @@ public class SslConnection extends AbstractConnection } @Override - public synchronized boolean flush(ByteBuffer... appOuts) throws IOException + public boolean flush(ByteBuffer... appOuts) throws IOException { // The contract for flush does not require that all appOuts bytes are written // or even that any appOut bytes are written! If the connection is write block @@ -842,159 +840,164 @@ public class SslConnection extends AbstractConnection try { - if (_cannotAcceptMoreAppDataToFlush) + synchronized (this) { - if (_sslEngine.isOutboundDone()) - throw new EofException(new ClosedChannelException()); - return false; - } - - // We will need a network buffer - if (_encryptedOutput == null) - _encryptedOutput = _bufferPool.acquire(_sslEngine.getSession().getPacketBufferSize(), _encryptedDirectBuffers); - - while (true) - { - // 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; try { - wrapResult = _sslEngine.wrap(appOuts, _encryptedOutput); + if (_cannotAcceptMoreAppDataToFlush) + { + if (_sslEngine.isOutboundDone()) + throw new EofException(new ClosedChannelException()); + return false; + } + + // We will need a network buffer + if (_encryptedOutput == null) + _encryptedOutput = _bufferPool.acquire(_sslEngine.getSession().getPacketBufferSize(), _encryptedDirectBuffers); + + while (true) + { + // 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; + try + { + wrapResult = _sslEngine.wrap(appOuts, _encryptedOutput); + } + finally + { + BufferUtil.flipToFlush(_encryptedOutput, pos); + } + if (LOG.isDebugEnabled()) + LOG.debug("{} wrap {}", SslConnection.this, wrapResult.toString().replace('\n',' ')); + + Status wrapResultStatus = wrapResult.getStatus(); + + boolean allConsumed=true; + for (ByteBuffer b : appOuts) + if (BufferUtil.hasContent(b)) + allConsumed=false; + + // and deal with the results returned from the sslEngineWrap + switch (wrapResultStatus) + { + case CLOSED: + { + // The SSL engine has close, but there may be close handshake that needs to be written + if (BufferUtil.hasContent(_encryptedOutput)) + { + _cannotAcceptMoreAppDataToFlush = true; + getEndPoint().flush(_encryptedOutput); + getEndPoint().shutdownOutput(); + // 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 false; + } + // otherwise we have written, and the caller will close the underlying connection + else + { + getEndPoint().shutdownOutput(); + } + return allConsumed; + } + case BUFFER_UNDERFLOW: + { + throw new IllegalStateException(); + } + default: + { + if (LOG.isDebugEnabled()) + LOG.debug("{} wrap {} {}", SslConnection.this, wrapResultStatus, BufferUtil.toHexSummary(_encryptedOutput)); + + if (wrapResult.getHandshakeStatus() == HandshakeStatus.FINISHED && !_handshaken) + { + _handshaken = true; + if (LOG.isDebugEnabled()) + LOG.debug("{} {} handshake succeeded {}/{}", SslConnection.this, + _sslEngine.getUseClientMode() ? "resumed client" : "server", + _sslEngine.getSession().getProtocol(),_sslEngine.getSession().getCipherSuite()); + notifyHandshakeSucceeded(_sslEngine); + } + + HandshakeStatus handshakeStatus = _sslEngine.getHandshakeStatus(); + + // Check whether renegotiation is allowed + if (_handshaken && handshakeStatus != HandshakeStatus.NOT_HANDSHAKING && !isRenegotiationAllowed()) + { + if (LOG.isDebugEnabled()) + LOG.debug("{} renegotiation denied", SslConnection.this); + getEndPoint().shutdownOutput(); + return allConsumed; + } + + // if we have net bytes, let's try to flush them + if (BufferUtil.hasContent(_encryptedOutput)) + if (!getEndPoint().flush(_encryptedOutput)) + getEndPoint().flush(_encryptedOutput); // one retry + + // But we also might have more to do for the handshaking state. + switch (handshakeStatus) + { + case NOT_HANDSHAKING: + // If we have not consumed all and had just finished handshaking, then we may + // have just flushed the last handshake in the encrypted buffers, so we should + // try again. + if (!allConsumed && wrapResult.getHandshakeStatus()==HandshakeStatus.FINISHED && BufferUtil.isEmpty(_encryptedOutput)) + continue; + + // Return true if we consumed all the bytes and encrypted are all flushed + return allConsumed && BufferUtil.isEmpty(_encryptedOutput); + + case NEED_TASK: + // run the task and continue + _sslEngine.getDelegatedTask().run(); + continue; + + case NEED_WRAP: + // Hey we just wrapped! Oh well who knows what the sslEngine is thinking, so continue and we will wrap again + continue; + + case NEED_UNWRAP: + // 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 && !getFillInterest().isInterested()) + { + // Tell the onFillable method that there might be a write to complete + _flushRequiresFillToProgress = true; + fill(BufferUtil.EMPTY_BUFFER); + // Check if after the fill() we need to wrap again + if (_sslEngine.getHandshakeStatus() == HandshakeStatus.NEED_WRAP) + continue; + } + return allConsumed && BufferUtil.isEmpty(_encryptedOutput); + + case FINISHED: + throw new IllegalStateException(); + } + } + } + } + } + catch (SSLHandshakeException x) + { + notifyHandshakeFailed(_sslEngine, x); + throw x; } finally { - BufferUtil.flipToFlush(_encryptedOutput, pos); - } - if (LOG.isDebugEnabled()) - LOG.debug("{} wrap {}", SslConnection.this, wrapResult.toString().replace('\n',' ')); - - Status wrapResultStatus = wrapResult.getStatus(); - - boolean allConsumed=true; - for (ByteBuffer b : appOuts) - if (BufferUtil.hasContent(b)) - allConsumed=false; - - // and deal with the results returned from the sslEngineWrap - switch (wrapResultStatus) - { - case CLOSED: - { - // The SSL engine has close, but there may be close handshake that needs to be written - if (BufferUtil.hasContent(_encryptedOutput)) - { - _cannotAcceptMoreAppDataToFlush = true; - getEndPoint().flush(_encryptedOutput); - getEndPoint().shutdownOutput(); - // 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 false; - } - // otherwise we have written, and the caller will close the underlying connection - else - { - getEndPoint().shutdownOutput(); - } - return allConsumed; - } - case BUFFER_UNDERFLOW: - { - throw new IllegalStateException(); - } - default: - { - if (LOG.isDebugEnabled()) - LOG.debug("{} wrap {} {}", SslConnection.this, wrapResultStatus, BufferUtil.toHexSummary(_encryptedOutput)); - - if (wrapResult.getHandshakeStatus() == HandshakeStatus.FINISHED && !_handshaken) - { - _handshaken = true; - if (LOG.isDebugEnabled()) - LOG.debug("{} {} handshake succeeded {}/{}", SslConnection.this, - _sslEngine.getUseClientMode() ? "resumed client" : "server", - _sslEngine.getSession().getProtocol(),_sslEngine.getSession().getCipherSuite()); - notifyHandshakeSucceeded(_sslEngine); - } - - HandshakeStatus handshakeStatus = _sslEngine.getHandshakeStatus(); - - // Check whether renegotiation is allowed - if (_handshaken && handshakeStatus != HandshakeStatus.NOT_HANDSHAKING && !isRenegotiationAllowed()) - { - if (LOG.isDebugEnabled()) - LOG.debug("{} renegotiation denied", SslConnection.this); - getEndPoint().shutdownOutput(); - return allConsumed; - } - - // if we have net bytes, let's try to flush them - if (BufferUtil.hasContent(_encryptedOutput)) - if (!getEndPoint().flush(_encryptedOutput)) - getEndPoint().flush(_encryptedOutput); // one retry - - // But we also might have more to do for the handshaking state. - switch (handshakeStatus) - { - case NOT_HANDSHAKING: - // If we have not consumed all and had just finished handshaking, then we may - // have just flushed the last handshake in the encrypted buffers, so we should - // try again. - if (!allConsumed && wrapResult.getHandshakeStatus()==HandshakeStatus.FINISHED && BufferUtil.isEmpty(_encryptedOutput)) - continue; - - // Return true if we consumed all the bytes and encrypted are all flushed - return allConsumed && BufferUtil.isEmpty(_encryptedOutput); - - case NEED_TASK: - // run the task and continue - _sslEngine.getDelegatedTask().run(); - continue; - - case NEED_WRAP: - // Hey we just wrapped! Oh well who knows what the sslEngine is thinking, so continue and we will wrap again - continue; - - case NEED_UNWRAP: - // 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 && !getFillInterest().isInterested()) - { - // Tell the onFillable method that there might be a write to complete - _flushRequiresFillToProgress = true; - fill(BufferUtil.EMPTY_BUFFER); - // Check if after the fill() we need to wrap again - if (_sslEngine.getHandshakeStatus() == HandshakeStatus.NEED_WRAP) - continue; - } - return allConsumed && BufferUtil.isEmpty(_encryptedOutput); - - case FINISHED: - throw new IllegalStateException(); - } + releaseEncryptedOutputBuffer(); } } - } - } - catch (SSLHandshakeException x) - { - notifyHandshakeFailed(_sslEngine, x); - close(x); - throw x; } catch (Throwable x) { close(x); throw x; } - finally - { - releaseEncryptedOutputBuffer(); - } } private void releaseEncryptedOutputBuffer() @@ -1035,9 +1038,8 @@ public class SslConnection extends AbstractConnection // TODO review close logic here if (ishut) close = true; - } - + if (flush) flush(BufferUtil.EMPTY_BUFFER); // Send the TLS close message. if (close) @@ -1051,7 +1053,7 @@ public class SslConnection extends AbstractConnection getEndPoint().close(); } } - + private void ensureFillInterested() { if (LOG.isDebugEnabled()) @@ -1128,5 +1130,4 @@ public class SslConnection extends AbstractConnection return super.toString()+"->"+getEndPoint().toString(); } } - }