diff --git a/modules/org.restlet.ext.nio/src/org/restlet/ext/nio/internal/channel/WritableSslChannel.java b/modules/org.restlet.ext.nio/src/org/restlet/ext/nio/internal/channel/WritableSslChannel.java index b150145422..ce682308cd 100644 --- a/modules/org.restlet.ext.nio/src/org/restlet/ext/nio/internal/channel/WritableSslChannel.java +++ b/modules/org.restlet.ext.nio/src/org/restlet/ext/nio/internal/channel/WritableSslChannel.java @@ -113,8 +113,22 @@ public void onCompleted() { public int onFill(Buffer buffer, Object... args) throws IOException { int srcSize = buffer.remaining(); ByteBuffer applicationBuffer = (ByteBuffer) args[0]; - SSLEngineResult sslResult = getConnection().getSslEngine().wrap( - applicationBuffer, buffer.getBytes()); + + HandshakeStatus handshakeStatus = getConnection().getSslHandshakeStatus(); + SSLEngineResult sslResult; + + // Empty buffers should generally only be passed to SSLEngine during the handshaking process, + // when SSLEngine will be generating handshake packets itself. The behavior of SSLEngine when + // an empty buffer is passed during normal operation varies across platforms. To avoid problems + // due to this inconsistency, we avoid calling SSLEngine with empty buffers when not handshaking, + // and return what J2SE's SSLEngine would have in that case. See the following issue for more details: + // https://github.com/restlet/restlet-framework-java/issues/852 + if (applicationBuffer.hasRemaining() || handshakeStatus != HandshakeStatus.NOT_HANDSHAKING) { + sslResult = getConnection().getSslEngine().wrap(applicationBuffer, buffer.getBytes()); + } else { + sslResult = new SSLEngineResult(Status.BUFFER_OVERFLOW, handshakeStatus, 0, 0); + } + getConnection().setSslResult(sslResult); return srcSize - buffer.remaining(); } diff --git a/modules/org.restlet.ext.nio/src/org/restlet/ext/nio/internal/connection/SslConnection.java b/modules/org.restlet.ext.nio/src/org/restlet/ext/nio/internal/connection/SslConnection.java index f7adadbd48..8541c1c959 100644 --- a/modules/org.restlet.ext.nio/src/org/restlet/ext/nio/internal/connection/SslConnection.java +++ b/modules/org.restlet.ext.nio/src/org/restlet/ext/nio/internal/connection/SslConnection.java @@ -78,6 +78,9 @@ public class SslConnection extends Connection { /** The engine result. */ private volatile SSLEngineResult sslEngineResult; + /** Whether a handshake is in progress. */ + private volatile boolean isHandshaking; + /** * Constructor. * @@ -275,8 +278,25 @@ private void handleSslHandshake() throws IOException { getLogger().log(Level.FINER, "Handling SSL handshake: " + hs); } + if (isHandshaking && hs == HandshakeStatus.NOT_HANDSHAKING) { + // In some cases, on some platforms, the engine can go directly from a handshaking state to NOT_HANDSHAKING. + // We handle this situation as if it had returned FINISHED. See the following issues: + // https://github.com/restlet/restlet-framework-java/issues/852 + // and + // https://github.com/restlet/restlet-framework-java/issues/862 + hs = HandshakeStatus.FINISHED; + + if (getLogger().isLoggable(Level.FINER)) { + getLogger().log(Level.FINER, "SSLEngine went directly from handshaking to NOT_HANDSHAKING, " + + "treating as FINISHED."); + } + + } + if (hs != HandshakeStatus.NOT_HANDSHAKING) { - switch (getSslHandshakeStatus()) { + isHandshaking = true; + + switch (hs) { case FINISHED: onFinished(); break; @@ -371,6 +391,8 @@ public boolean isSslHandshaking() { * exchanged. */ private void onFinished() { + isHandshaking = false; + if (isClientSide()) { getInboundWay().setIoState(IoState.IDLE); getOutboundWay().setIoState(IoState.INTEREST); diff --git a/modules/org.restlet/src/org/restlet/engine/ssl/DefaultSslContextFactory.java b/modules/org.restlet/src/org/restlet/engine/ssl/DefaultSslContextFactory.java index ee444ce008..d1b7addff2 100644 --- a/modules/org.restlet/src/org/restlet/engine/ssl/DefaultSslContextFactory.java +++ b/modules/org.restlet/src/org/restlet/engine/ssl/DefaultSslContextFactory.java @@ -686,7 +686,7 @@ public void init(Series helperParameters) { enabledProtocols.toArray(enabledProtocolsArray); setEnabledProtocols(enabledProtocolsArray); } else { - setEnabledCipherSuites(null); + setEnabledProtocols(null); } setKeyManagerAlgorithm(helperParameters.getFirstValue(