Merged branch 'jetty-9.4.x' into 'master'.
This commit is contained in:
commit
b5d2b73f07
|
@ -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
|
||||
|
|
|
@ -23,4 +23,5 @@ include::troubleshooting-zip-exceptions.adoc[]
|
|||
include::troubleshooting-locked-files.adoc[]
|
||||
include::preventing-memory-leaks.adoc[]
|
||||
include::slow-deployment.adoc[]
|
||||
include::security-reports.adoc[]
|
||||
include::security-reports.adoc[]
|
||||
include::watchservice.adoc[]
|
||||
|
|
|
@ -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.
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ logback
|
|||
|
||||
[depend]
|
||||
server
|
||||
logback-core
|
||||
logback-impl
|
||||
resources
|
||||
|
||||
[provide]
|
||||
|
|
Loading…
Reference in New Issue