Merged branch 'jetty-9.4.x' into 'master'.

This commit is contained in:
Simone Bordet 2016-12-20 11:55:39 +01:00
commit b5d2b73f07
5 changed files with 413 additions and 376 deletions

View File

@ -17,19 +17,19 @@
[[what-jetty-version]] [[what-jetty-version]]
=== What Version Do I Use? === What Version Do I Use?
Jetty 9 is the most recent version of Jetty and has a great many improvements over previous versions. 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. 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. 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 .Jetty Versions
[width="100%",cols="12%,9%,15%,6%,21%,10%,6%,21%",options="header",] [width="100%",cols="12%,9%,15%,6%,21%,10%,6%,21%",options="header",]
|======================================================================= |=======================================================================
|Version |Year |Home |JVM |Protocols |Servlet |JSP |Status |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.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 |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 |2009-2014 |Eclipse/Codehaus |1.6 |HTTP/1.1 RFC2616, WebSocket RFC 6455, SPDY v3 |3.0 |2.2 |Deprecated
|8.1 |2009- |Eclipse/Codehaus |1.6 |HTTP/1.1 RFC2616, WebSocket RFC 6455, SPDY v3 |3.0 |2.2 |Venerable |7 |2008-2014 |Eclipse/Codehaus |1.5 |HTTP/1.1 RFC2616, WebSocket RFC 6455, SPDY v3 |2.5 |2.1 |Deprecated
|7 |2008- |Eclipse/Codehaus |1.5 |HTTP/1.1 RFC2616, WebSocket RFC 6455, SPDY v3 |2.5 |2.1 |Venerable
|6 |2006-2010 |Codehaus |1.4-1.5 |HTTP/1.1 RFC2616 |2.5 |2.0 |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 |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 |4 |2001-2006 |Sourceforge |1.2, J2ME |HTTP/1.1 RFC2616 |2.3 |1.2 |Ancient

View File

@ -23,4 +23,5 @@ include::troubleshooting-zip-exceptions.adoc[]
include::troubleshooting-locked-files.adoc[] include::troubleshooting-locked-files.adoc[]
include::preventing-memory-leaks.adoc[] include::preventing-memory-leaks.adoc[]
include::slow-deployment.adoc[] include::slow-deployment.adoc[]
include::security-reports.adoc[] include::security-reports.adoc[]
include::watchservice.adoc[]

View File

@ -0,0 +1,35 @@
// ========================================================================
// Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
// ========================================================================
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
[[watchservice]]
=== Java WatchService
The JVM link:https://docs.oracle.com/javase/7/docs/api/java/nio/file/WatchService.html[`WatchService`] is in place to monitor objects like a directory for changes, and then update it's contents and notify the application of those changes.
This service is useful for features like link:#hot-deployment[Hot Deployment].
When a change is detected, the `WatchService` will enter a "quiet time" where it is waiting for the change (or changes) to be made and completed before notifying the application of the change.
Example:
A new war file is copied into `/webapps`.
The `WatchService` can (depending on implementation) see that the file was created (which is registered as an event!, and that its growing in size (another event).
With the quiet time, each of the events are gated behind that timeout before the aggregated events are sent to the application.
While some operating systems such as Windows have a native value for this quiet time, not all do, notably OSX.
At the core this is a limitation of the JVM's FileSystem-specific implementation, but one that has been raised to the link:https://bugs.openjdk.java.net/browse/JDK-7133447[attention of the project.]
==== Remedy
To help offset the delay in systems like OSX, Jetty defaults the value for non-native implementations to a link:{GITBROWSEURL}/jetty-util/src/main/java/org/eclipse/jetty/util/PathWatcher.java#L1431[time of 5000ms.]
Using values lower than 5000ms is not recommended and has shown to frequently fail.

View File

@ -92,8 +92,7 @@ public class SslConnection extends AbstractConnection
private final boolean _decryptedDirectBuffers = false; private final boolean _decryptedDirectBuffers = false;
private boolean _renegotiationAllowed; private boolean _renegotiationAllowed;
private boolean _closedOutbound; private boolean _closedOutbound;
private abstract class RunnableTask implements Runnable, Invocable private abstract class RunnableTask implements Runnable, Invocable
{ {
private final String _operation; private final String _operation;
@ -109,7 +108,7 @@ public class SslConnection extends AbstractConnection
return String.format("SSL:%s:%s:%s",SslConnection.this,_operation,getInvocationType()); return String.format("SSL:%s:%s:%s",SslConnection.this,_operation,getInvocationType());
} }
} }
private final Runnable _runCompleteWrite = new RunnableTask("runCompleteWrite") private final Runnable _runCompleteWrite = new RunnableTask("runCompleteWrite")
{ {
@Override @Override
@ -117,14 +116,14 @@ public class SslConnection extends AbstractConnection
{ {
_decryptedEndPoint.getWriteFlusher().completeWrite(); _decryptedEndPoint.getWriteFlusher().completeWrite();
} }
@Override @Override
public InvocationType getInvocationType() public InvocationType getInvocationType()
{ {
return getDecryptedEndPoint().getWriteFlusher().getCallbackInvocationType(); return getDecryptedEndPoint().getWriteFlusher().getCallbackInvocationType();
} }
}; };
private final Runnable _runFillable = new RunnableTask("runFillable") private final Runnable _runFillable = new RunnableTask("runFillable")
{ {
@Override @Override
@ -132,15 +131,15 @@ public class SslConnection extends AbstractConnection
{ {
_decryptedEndPoint.getFillInterest().fillable(); _decryptedEndPoint.getFillInterest().fillable();
} }
@Override @Override
public InvocationType getInvocationType() public InvocationType getInvocationType()
{ {
return getDecryptedEndPoint().getFillInterest().getCallbackInvocationType(); return getDecryptedEndPoint().getFillInterest().getCallbackInvocationType();
} }
}; };
Callback _sslReadCallback = new Callback() private final Callback _sslReadCallback = new Callback()
{ {
@Override @Override
public void succeeded() public void succeeded()
@ -309,12 +308,12 @@ public class SslConnection extends AbstractConnection
int di=b==null?-1:b.remaining(); int di=b==null?-1:b.remaining();
return String.format("%s@%x{%s,eio=%d/%d,di=%d}=>%s", return String.format("%s@%x{%s,eio=%d/%d,di=%d}=>%s",
getClass().getSimpleName(), getClass().getSimpleName(),
hashCode(), hashCode(),
_sslEngine.getHandshakeStatus(), _sslEngine.getHandshakeStatus(),
ei,eo,di, ei,eo,di,
((AbstractConnection)_decryptedEndPoint.getConnection()).toConnectionString()); ((AbstractConnection)_decryptedEndPoint.getConnection()).toConnectionString());
} }
public class DecryptedEndPoint extends AbstractEndPoint 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 // 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 // only if there is an active writeflusher or a read needed to write
// data. In either case the appropriate callback is passed on. // data. In either case the appropriate callback is passed on.
boolean fail_filler = false; boolean fail_filler;
synchronized (DecryptedEndPoint.this) synchronized (DecryptedEndPoint.this)
{ {
if (LOG.isDebugEnabled()) if (LOG.isDebugEnabled())
LOG.debug("{} write.failed", SslConnection.this, x); LOG.debug("{} write failed", SslConnection.this, x);
BufferUtil.clear(_encryptedOutput); BufferUtil.clear(_encryptedOutput);
releaseEncryptedOutputBuffer(); releaseEncryptedOutputBuffer();
_cannotAcceptMoreAppDataToFlush = false; _cannotAcceptMoreAppDataToFlush = false;
fail_filler = _fillRequiresFlushToProgress;
if (_fillRequiresFlushToProgress) if (_fillRequiresFlushToProgress)
{
_fillRequiresFlushToProgress = false; _fillRequiresFlushToProgress = false;
fail_filler = true;
}
} }
final boolean filler_failed=fail_filler;
failedCallback(new Callback() failedCallback(new Callback()
{ {
@Override @Override
public void failed(Throwable x) public void failed(Throwable x)
{ {
if (filler_failed) if (fail_filler)
getFillInterest().onFail(x); getFillInterest().onFail(x);
getWriteFlusher().onFail(x); getWriteFlusher().onFail(x);
} }
},x); }, x);
} }
@Override @Override
@ -389,7 +384,7 @@ public class SslConnection extends AbstractConnection
{ {
return getWriteFlusher().getCallbackInvocationType(); return getWriteFlusher().getCallbackInvocationType();
} }
@Override @Override
public String toString() public String toString()
{ {
@ -473,7 +468,7 @@ public class SslConnection extends AbstractConnection
else if (_sslEngine.getHandshakeStatus() == HandshakeStatus.NEED_UNWRAP) else if (_sslEngine.getHandshakeStatus() == HandshakeStatus.NEED_UNWRAP)
{ {
// check if we are actually read blocked in order to write // check if we are actually read blocked in order to write
_flushRequiresFillToProgress = true; _flushRequiresFillToProgress = true;
need_fill_interest = !SslConnection.this.isFillInterested(); need_fill_interest = !SslConnection.this.isFillInterested();
} }
else else
@ -488,7 +483,7 @@ public class SslConnection extends AbstractConnection
} }
if (write) if (write)
getEndPoint().write(_writeCallback, _encryptedOutput); getEndPoint().write(_writeCallback, _encryptedOutput);
else if (need_fill_interest) else if (need_fill_interest)
ensureFillInterested(); ensureFillInterested();
else if (try_again) else if (try_again)
@ -507,7 +502,6 @@ public class SslConnection extends AbstractConnection
getExecutor().execute(_runCompleteWrite); getExecutor().execute(_runCompleteWrite);
} }
} }
} }
@Override @Override
@ -557,7 +551,7 @@ public class SslConnection extends AbstractConnection
getEndPoint().write(_writeCallback, _encryptedOutput); getEndPoint().write(_writeCallback, _encryptedOutput);
else if (fillable) else if (fillable)
getExecutor().execute(_runFillable); getExecutor().execute(_runFillable);
else else
ensureFillInterested(); ensureFillInterested();
} }
@ -579,100 +573,191 @@ public class SslConnection extends AbstractConnection
} }
@Override @Override
public synchronized int fill(ByteBuffer buffer) throws IOException public int fill(ByteBuffer buffer) throws IOException
{ {
try try
{ {
// Do we already have some decrypted data? synchronized (this)
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)
{ {
// Let's try reading some encrypted data... even if we have some already. try
int net_filled = getEndPoint().fill(_encryptedInput);
decryption: while (true)
{ {
// Let's unwrap even if we have no net data because in that // Do we already have some decrypted data?
// case we want to fall through to the handshake handling if (BufferUtil.hasContent(_decryptedInput))
int pos = BufferUtil.flipToFill(app_in); return BufferUtil.append(buffer,_decryptedInput);
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));
}
HandshakeStatus handshakeStatus = _sslEngine.getHandshakeStatus(); // We will need a network buffer
HandshakeStatus unwrapHandshakeStatus = unwrapResult.getHandshakeStatus(); if (_encryptedInput == null)
Status unwrapResultStatus = unwrapResult.getStatus(); _encryptedInput = _bufferPool.acquire(_sslEngine.getSession().getPacketBufferSize(), _encryptedDirectBuffers);
else
BufferUtil.compact(_encryptedInput);
// Extra check on unwrapResultStatus == OK with zero bytes consumed // We also need an app buffer, but can use the passed buffer if it is big enough
// or produced is due to an SSL client on Android (see bug #454773). ByteBuffer app_in;
_underFlown = unwrapResultStatus == Status.BUFFER_UNDERFLOW || if (BufferUtil.space(buffer) > _sslEngine.getSession().getApplicationBufferSize())
unwrapResultStatus == Status.OK && unwrapResult.bytesConsumed() == 0 && unwrapResult.bytesProduced() == 0; 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) // Let's try reading some encrypted data... even if we have some already.
closeInbound(); int net_filled = getEndPoint().fill(_encryptedInput);
if (net_filled <= 0)
return net_filled;
}
switch (unwrapResultStatus) decryption: while (true)
{
case CLOSED:
{ {
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 switch (handshakeStatus)
return -1; {
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(); if (unwrapHandshakeStatus == HandshakeStatus.FINISHED && !_handshaken)
continue; {
} _handshaken = true;
case NEED_WRAP: if (LOG.isDebugEnabled())
{ LOG.debug("{} {} handshake succeeded {}/{}", SslConnection.this,
// We need to send some handshake data (probably the close handshake). _sslEngine.getUseClientMode() ? "client" : "resumed server",
// We return -1 so that the application can drive the close by flushing _sslEngine.getSession().getProtocol(),_sslEngine.getSession().getCipherSuite());
// or shutting down the output. notifyHandshakeSucceeded(_sslEngine);
return -1; }
}
case NEED_UNWRAP: // Check whether renegotiation is allowed
{ if (_handshaken && handshakeStatus != HandshakeStatus.NOT_HANDSHAKING && !isRenegotiationAllowed())
// We expected to read more, but we got closed. {
// Return -1 to indicate to the application to drive the close. if (LOG.isDebugEnabled())
return -1; 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: default:
{ {
@ -680,136 +765,49 @@ public class SslConnection extends AbstractConnection
} }
} }
} }
case BUFFER_UNDERFLOW: }
case OK: }
{ catch (SSLHandshakeException x)
if (unwrapHandshakeStatus == HandshakeStatus.FINISHED && !_handshaken) {
{ notifyHandshakeFailed(_sslEngine, x);
_handshaken = true; throw x;
if (LOG.isDebugEnabled()) }
LOG.debug("{} {} handshake succeeded {}/{}", SslConnection.this, catch (SSLException x)
_sslEngine.getUseClientMode() ? "client" : "resumed server", {
_sslEngine.getSession().getProtocol(),_sslEngine.getSession().getCipherSuite()); if (!_handshaken)
notifyHandshakeSucceeded(_sslEngine); {
} x = (SSLException)new SSLHandshakeException(x.getMessage()).initCause(x);
notifyHandshakeFailed(_sslEngine, x);
// Check whether renegotiation is allowed }
if (_handshaken && handshakeStatus != HandshakeStatus.NOT_HANDSHAKING && !isRenegotiationAllowed()) throw x;
{ }
if (LOG.isDebugEnabled()) finally
LOG.debug("{} renegotiation denied", SslConnection.this); {
closeInbound(); // If we are handshaking, then wake up any waiting write as well as it may have been blocked on the read
return -1; if (_flushRequiresFillToProgress)
} {
_flushRequiresFillToProgress = false;
// If bytes were produced, don't bother with the handshake status; getExecutor().execute(_runCompleteWrite);
// pass the decrypted data to the application, which will perform }
// another call to fill() or flush().
if (unwrapResult.bytesProduced() > 0) if (_encryptedInput != null && !_encryptedInput.hasRemaining())
{ {
if (app_in == buffer) _bufferPool.release(_encryptedInput);
return unwrapResult.bytesProduced(); _encryptedInput = null;
return BufferUtil.append(buffer,_decryptedInput); }
} if (_decryptedInput != null && !_decryptedInput.hasRemaining())
{
switch (handshakeStatus) _bufferPool.release(_decryptedInput);
{ _decryptedInput = null;
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);
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) catch (Throwable x)
{ {
close(x); close(x);
throw 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() private void closeInbound()
@ -825,7 +823,7 @@ public class SslConnection extends AbstractConnection
} }
@Override @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 // 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 // or even that any appOut bytes are written! If the connection is write block
@ -842,159 +840,164 @@ public class SslConnection extends AbstractConnection
try 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 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 finally
{ {
BufferUtil.flipToFlush(_encryptedOutput, pos); releaseEncryptedOutputBuffer();
}
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);
close(x);
throw x;
} }
catch (Throwable x) catch (Throwable x)
{ {
close(x); close(x);
throw x; throw x;
} }
finally
{
releaseEncryptedOutputBuffer();
}
} }
private void releaseEncryptedOutputBuffer() private void releaseEncryptedOutputBuffer()
@ -1035,9 +1038,8 @@ public class SslConnection extends AbstractConnection
// TODO review close logic here // TODO review close logic here
if (ishut) if (ishut)
close = true; close = true;
} }
if (flush) if (flush)
flush(BufferUtil.EMPTY_BUFFER); // Send the TLS close message. flush(BufferUtil.EMPTY_BUFFER); // Send the TLS close message.
if (close) if (close)
@ -1051,7 +1053,7 @@ public class SslConnection extends AbstractConnection
getEndPoint().close(); getEndPoint().close();
} }
} }
private void ensureFillInterested() private void ensureFillInterested()
{ {
if (LOG.isDebugEnabled()) if (LOG.isDebugEnabled())
@ -1128,5 +1130,4 @@ public class SslConnection extends AbstractConnection
return super.toString()+"->"+getEndPoint().toString(); return super.toString()+"->"+getEndPoint().toString();
} }
} }
} }

View File

@ -8,7 +8,7 @@ logback
[depend] [depend]
server server
logback-core logback-impl
resources resources
[provide] [provide]