SSL refactorings.
This commit is contained in:
parent
7f291e8831
commit
9e8b2f1aad
|
@ -76,14 +76,14 @@ public abstract class AbstractAsyncConnection implements AsyncConnection
|
||||||
else
|
else
|
||||||
_endp.shutdownOutput();
|
_endp.shutdownOutput();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
public void scheduleOnReadable()
|
public void scheduleOnReadable()
|
||||||
{
|
{
|
||||||
if (_readInterested.compareAndSet(false,true))
|
if (_readInterested.compareAndSet(false,true))
|
||||||
getEndPoint().readable(null,_readCallback);
|
getEndPoint().readable(null,_readCallback);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
public void onReadFail(Throwable cause)
|
public void onReadFail(Throwable cause)
|
||||||
{
|
{
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
// ========================================================================
|
||||||
|
// Copyright (c) 2012 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.
|
||||||
|
// ========================================================================
|
||||||
|
|
||||||
|
package org.eclipse.jetty.io.ssl;
|
||||||
|
|
||||||
|
enum ReadState
|
||||||
|
{
|
||||||
|
HANDSHAKING, HANDSHAKEN, IDLE, UNDERFLOW, DECRYPTED, CLOSED
|
||||||
|
}
|
|
@ -0,0 +1,265 @@
|
||||||
|
package org.eclipse.jetty.io.ssl;
|
||||||
|
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import javax.net.ssl.SSLEngine;
|
||||||
|
import javax.net.ssl.SSLEngineResult;
|
||||||
|
import javax.net.ssl.SSLException;
|
||||||
|
|
||||||
|
import org.eclipse.jetty.util.BufferUtil;
|
||||||
|
import org.eclipse.jetty.util.log.Log;
|
||||||
|
import org.eclipse.jetty.util.log.Logger;
|
||||||
|
|
||||||
|
public abstract class SSLMachine
|
||||||
|
{
|
||||||
|
private static final Logger logger = Log.getLogger(SslConnection.class);
|
||||||
|
private static final ByteBuffer EMPTY_BUFFER = ByteBuffer.allocate(0);
|
||||||
|
private final Object wrapLock = new Object();
|
||||||
|
private final SSLEngine engine;
|
||||||
|
private boolean handshaken;
|
||||||
|
private boolean remoteClosed;
|
||||||
|
|
||||||
|
public SSLMachine(SSLEngine engine)
|
||||||
|
{
|
||||||
|
this.engine = engine;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ReadState decrypt(ByteBuffer netInput, ByteBuffer appInput) throws SSLException
|
||||||
|
{
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
SSLEngineResult.HandshakeStatus handshakeStatus = engine.getHandshakeStatus();
|
||||||
|
logger.debug("Reading from {}, handshake status: {}", netInput, handshakeStatus);
|
||||||
|
switch (handshakeStatus)
|
||||||
|
{
|
||||||
|
case NEED_UNWRAP:
|
||||||
|
{
|
||||||
|
ReadState result = unwrap(netInput, appInput);
|
||||||
|
if (result == null)
|
||||||
|
break;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
case NEED_TASK:
|
||||||
|
{
|
||||||
|
executeTasks();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case NEED_WRAP:
|
||||||
|
{
|
||||||
|
writeForDecrypt(EMPTY_BUFFER);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case NOT_HANDSHAKING:
|
||||||
|
{
|
||||||
|
ReadState result = unwrap(netInput, appInput);
|
||||||
|
if (result == null)
|
||||||
|
break;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
throw new IllegalStateException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public WriteState encrypt(ByteBuffer appOutput, ByteBuffer netOutput) throws SSLException
|
||||||
|
{
|
||||||
|
return wrap(appOutput, netOutput);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract void writeForDecrypt(ByteBuffer appOutput);
|
||||||
|
|
||||||
|
private ReadState unwrap(ByteBuffer netInput, ByteBuffer appInput) throws SSLException
|
||||||
|
{
|
||||||
|
boolean decrypted = false;
|
||||||
|
while (netInput.hasRemaining())
|
||||||
|
{
|
||||||
|
logger.debug("Decrypting from {} to {}", netInput, appInput);
|
||||||
|
SSLEngineResult result = unwrap(engine, netInput, appInput);
|
||||||
|
logger.debug("Decrypted from {} to {}, result {}", netInput, appInput, result);
|
||||||
|
switch (result.getStatus())
|
||||||
|
{
|
||||||
|
case OK:
|
||||||
|
{
|
||||||
|
SSLEngineResult.HandshakeStatus handshakeStatus = result.getHandshakeStatus();
|
||||||
|
if (handshakeStatus == SSLEngineResult.HandshakeStatus.FINISHED)
|
||||||
|
{
|
||||||
|
if (engine.getUseClientMode())
|
||||||
|
{
|
||||||
|
logger.debug("Handshake finished (client), new SSL session");
|
||||||
|
handshaken = true;
|
||||||
|
return ReadState.HANDSHAKEN;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (handshaken)
|
||||||
|
{
|
||||||
|
logger.debug("Rehandshake finished (server)");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
logger.debug("Handshake finished (server), cached SSL session");
|
||||||
|
handshaken = true;
|
||||||
|
return ReadState.HANDSHAKEN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.bytesProduced() > 0)
|
||||||
|
{
|
||||||
|
decrypted = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (handshakeStatus == SSLEngineResult.HandshakeStatus.NEED_UNWRAP)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (handshakeStatus == SSLEngineResult.HandshakeStatus.FINISHED ||
|
||||||
|
handshakeStatus == SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING)
|
||||||
|
break;
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
case BUFFER_UNDERFLOW:
|
||||||
|
{
|
||||||
|
return decrypted ? ReadState.DECRYPTED : ReadState.UNDERFLOW;
|
||||||
|
}
|
||||||
|
case CLOSED:
|
||||||
|
{
|
||||||
|
// We have read the SSL close message and updated the SSLEngine
|
||||||
|
logger.debug("Close alert received from remote peer");
|
||||||
|
remoteClosed = true;
|
||||||
|
return decrypted ? ReadState.DECRYPTED : ReadState.CLOSED;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
throw new IllegalStateException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SSLEngineResult.HandshakeStatus handshakeStatus = engine.getHandshakeStatus();
|
||||||
|
if (handshakeStatus == SSLEngineResult.HandshakeStatus.NEED_UNWRAP ||
|
||||||
|
engine.getHandshakeStatus() == SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING)
|
||||||
|
return ReadState.UNDERFLOW;
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private SSLEngineResult unwrap(SSLEngine engine, ByteBuffer netInput, ByteBuffer appInput) throws SSLException
|
||||||
|
{
|
||||||
|
int position = BufferUtil.flipToFill(appInput);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return engine.unwrap(netInput, appInput);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
BufferUtil.flipToFlush(appInput, position);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void executeTasks()
|
||||||
|
{
|
||||||
|
Runnable task;
|
||||||
|
while ((task = engine.getDelegatedTask()) != null)
|
||||||
|
{
|
||||||
|
task.run();
|
||||||
|
logger.debug("Executed task: {}", task);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private WriteState wrap(ByteBuffer appOutput, ByteBuffer netOutput) throws SSLException
|
||||||
|
{
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
// Locking is important because application code may call handshake() (to rehandshake)
|
||||||
|
// followed by encrypt(). In this case, the handshake() call may need to wrap, then
|
||||||
|
// unwrap and then wrap again to perform the handshake, and the second wrap may be
|
||||||
|
// concurrent with the wrap triggered by encrypt(), which would corrupt SSLEngine.
|
||||||
|
// It is also important that wrap and write are atomic, because SSL packets needs to
|
||||||
|
// be ordered (and therefore the packet created first must be written before the packet
|
||||||
|
// created second).
|
||||||
|
SSLEngineResult result;
|
||||||
|
synchronized (wrapLock)
|
||||||
|
{
|
||||||
|
logger.debug("Encrypting from {} to {}", appOutput, netOutput);
|
||||||
|
result = wrap(engine, appOutput, netOutput);
|
||||||
|
logger.debug("Encrypted from {} to {}, result: {}", appOutput, netOutput, result);
|
||||||
|
|
||||||
|
/*
|
||||||
|
if (result.bytesProduced() > 0)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
netOutput.flip();
|
||||||
|
write(netOutput);
|
||||||
|
netOutput.clear();
|
||||||
|
}
|
||||||
|
catch (RuntimeIOException x)
|
||||||
|
{
|
||||||
|
// If we try to write the SSL close message but we cannot
|
||||||
|
// because the other peer has already closed the connection,
|
||||||
|
// then ignore the exception and continue
|
||||||
|
if (result.getStatus() != SSLEngineResult.Status.CLOSED)
|
||||||
|
throw x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.getStatus() == SSLEngineResult.Status.CLOSED)
|
||||||
|
return WriteState.CLOSED;
|
||||||
|
|
||||||
|
SSLEngineResult.HandshakeStatus handshakeStatus = result.getHandshakeStatus();
|
||||||
|
if (handshakeStatus == SSLEngineResult.HandshakeStatus.NEED_WRAP)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (handshakeStatus == SSLEngineResult.HandshakeStatus.FINISHED)
|
||||||
|
{
|
||||||
|
if (engine.getUseClientMode())
|
||||||
|
{
|
||||||
|
if (handshaken)
|
||||||
|
{
|
||||||
|
logger.debug("Rehandshake finished (client)");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
logger.debug("Handshake finished (client), cached SSL session");
|
||||||
|
handshaken = true;
|
||||||
|
return WriteState.HANDSHAKEN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
logger.debug("Handshake finished (server), new SSL session");
|
||||||
|
assert !appOutput.hasRemaining();
|
||||||
|
handshaken = true;
|
||||||
|
return WriteState.HANDSHAKEN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!appOutput.hasRemaining())
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private SSLEngineResult wrap(SSLEngine engine, ByteBuffer appOutput, ByteBuffer netOutput) throws SSLException
|
||||||
|
{
|
||||||
|
int position = BufferUtil.flipToFill(netOutput);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return engine.wrap(appOutput, netOutput);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
BufferUtil.flipToFlush(netOutput, position);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isRemoteClosed()
|
||||||
|
{
|
||||||
|
return remoteClosed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void close()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,624 @@
|
||||||
|
// ========================================================================
|
||||||
|
// Copyright (c) 2004-2011 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.
|
||||||
|
// ========================================================================
|
||||||
|
|
||||||
|
package org.eclipse.jetty.io.ssl;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.nio.channels.ReadPendingException;
|
||||||
|
import java.nio.channels.WritePendingException;
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
import javax.net.ssl.SSLEngine;
|
||||||
|
import javax.net.ssl.SSLException;
|
||||||
|
|
||||||
|
import org.eclipse.jetty.io.AbstractAsyncConnection;
|
||||||
|
import org.eclipse.jetty.io.AbstractEndPoint;
|
||||||
|
import org.eclipse.jetty.io.AsyncConnection;
|
||||||
|
import org.eclipse.jetty.io.AsyncEndPoint;
|
||||||
|
import org.eclipse.jetty.io.ByteBufferPool;
|
||||||
|
import org.eclipse.jetty.io.EndPoint;
|
||||||
|
import org.eclipse.jetty.io.RuntimeIOException;
|
||||||
|
import org.eclipse.jetty.io.SelectChannelEndPoint;
|
||||||
|
import org.eclipse.jetty.util.BufferUtil;
|
||||||
|
import org.eclipse.jetty.util.Callback;
|
||||||
|
import org.eclipse.jetty.util.log.Log;
|
||||||
|
import org.eclipse.jetty.util.log.Logger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An AsyncConnection that acts as an interceptor between and EndPoint and another
|
||||||
|
* Connection, that implements TLS encryption using an {@link SSLEngine}.
|
||||||
|
* <p>
|
||||||
|
* The connector uses an {@link EndPoint} (like {@link SelectChannelEndPoint}) as
|
||||||
|
* it's source/sink of encrypted data. It then provides {@link #getAppEndPoint()} to
|
||||||
|
* expose a source/sink of unencrypted data to another connection (eg HttpConnection).
|
||||||
|
*/
|
||||||
|
public class SslConnection extends AbstractAsyncConnection
|
||||||
|
{
|
||||||
|
private static final Logger logger = Log.getLogger(SslConnection.class);
|
||||||
|
private final ByteBufferPool byteBufferPool;
|
||||||
|
private final SSLEngine sslEngine;
|
||||||
|
private final SSLMachine sslMachine;
|
||||||
|
private final AsyncEndPoint appEndPoint;
|
||||||
|
private boolean direct = false;
|
||||||
|
private ReadState readState = ReadState.HANDSHAKING;
|
||||||
|
private WriteState writeState = WriteState.HANDSHAKING;
|
||||||
|
private ByteBuffer appInput;
|
||||||
|
private ByteBuffer netInput;
|
||||||
|
private ByteBuffer netOutput;
|
||||||
|
private Callback appReader;
|
||||||
|
private Object readContext;
|
||||||
|
|
||||||
|
public SslConnection(ByteBufferPool byteBufferPool, Executor executor, AsyncEndPoint endPoint, SSLEngine sslEngine)
|
||||||
|
{
|
||||||
|
super(endPoint, executor);
|
||||||
|
this.byteBufferPool = byteBufferPool;
|
||||||
|
this.sslEngine = sslEngine;
|
||||||
|
this.sslMachine = new ConnectionSSLMachine(sslEngine);
|
||||||
|
this.appEndPoint = new ApplicationEndPoint();
|
||||||
|
}
|
||||||
|
|
||||||
|
public SSLEngine getSSLEngine()
|
||||||
|
{
|
||||||
|
return sslEngine;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onOpen()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
super.onOpen();
|
||||||
|
scheduleOnReadable();
|
||||||
|
sslEngine.beginHandshake();
|
||||||
|
}
|
||||||
|
catch (SSLException x)
|
||||||
|
{
|
||||||
|
throw new RuntimeIOException(x);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public AsyncEndPoint getAppEndPoint()
|
||||||
|
{
|
||||||
|
return appEndPoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateReadState(ReadState newReadState)
|
||||||
|
{
|
||||||
|
ReadState oldReadState = readState;
|
||||||
|
switch (oldReadState)
|
||||||
|
{
|
||||||
|
case HANDSHAKING:
|
||||||
|
{
|
||||||
|
if (newReadState != ReadState.HANDSHAKEN)
|
||||||
|
throw wrongReadStateUpdate(oldReadState, newReadState);
|
||||||
|
readState = newReadState;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case HANDSHAKEN:
|
||||||
|
{
|
||||||
|
switch (newReadState)
|
||||||
|
{
|
||||||
|
case IDLE:
|
||||||
|
{
|
||||||
|
if (BufferUtil.hasContent(netInput))
|
||||||
|
throw wrongReadStateUpdate(oldReadState, newReadState);
|
||||||
|
readState = newReadState;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case UNDERFLOW:
|
||||||
|
{
|
||||||
|
if (!BufferUtil.hasContent(netInput))
|
||||||
|
throw wrongReadStateUpdate(oldReadState, newReadState);
|
||||||
|
readState = newReadState;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case DECRYPTED:
|
||||||
|
{
|
||||||
|
if (!BufferUtil.hasContent(appInput))
|
||||||
|
throw wrongReadStateUpdate(oldReadState, newReadState);
|
||||||
|
readState = newReadState;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CLOSED:
|
||||||
|
{
|
||||||
|
if (BufferUtil.hasContent(appInput))
|
||||||
|
throw wrongReadStateUpdate(oldReadState, newReadState);
|
||||||
|
readState = newReadState;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
throw wrongReadStateUpdate(oldReadState, newReadState);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case IDLE:
|
||||||
|
{
|
||||||
|
switch (newReadState)
|
||||||
|
{
|
||||||
|
case UNDERFLOW:
|
||||||
|
{
|
||||||
|
if (!BufferUtil.hasContent(netInput))
|
||||||
|
throw wrongReadStateUpdate(oldReadState, newReadState);
|
||||||
|
readState = newReadState;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case DECRYPTED:
|
||||||
|
{
|
||||||
|
if (!BufferUtil.hasContent(appInput))
|
||||||
|
throw wrongReadStateUpdate(oldReadState, newReadState);
|
||||||
|
readState = newReadState;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CLOSED:
|
||||||
|
{
|
||||||
|
if (BufferUtil.hasContent(appInput))
|
||||||
|
throw wrongReadStateUpdate(oldReadState, newReadState);
|
||||||
|
readState = newReadState;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
throw wrongReadStateUpdate(oldReadState, newReadState);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case DECRYPTED:
|
||||||
|
{
|
||||||
|
switch (newReadState)
|
||||||
|
{
|
||||||
|
case IDLE:
|
||||||
|
{
|
||||||
|
if (BufferUtil.hasContent(netInput))
|
||||||
|
throw wrongReadStateUpdate(oldReadState, newReadState);
|
||||||
|
readState = newReadState;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case UNDERFLOW:
|
||||||
|
{
|
||||||
|
if (!BufferUtil.hasContent(netInput))
|
||||||
|
throw wrongReadStateUpdate(oldReadState, newReadState);
|
||||||
|
readState = newReadState;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CLOSED:
|
||||||
|
{
|
||||||
|
if (BufferUtil.hasContent(appInput))
|
||||||
|
throw wrongReadStateUpdate(oldReadState, newReadState);
|
||||||
|
readState = newReadState;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
throw wrongReadStateUpdate(oldReadState, newReadState);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
throw wrongReadStateUpdate(oldReadState, newReadState);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private IllegalStateException wrongReadStateUpdate(ReadState oldReadState, ReadState newReadState)
|
||||||
|
{
|
||||||
|
String message = String.format("Invalid read state update: %s => %s", oldReadState, newReadState);
|
||||||
|
return new IllegalStateException(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onReadable()
|
||||||
|
{
|
||||||
|
if (appInput != null)
|
||||||
|
throw new IllegalStateException();
|
||||||
|
|
||||||
|
switch (readState)
|
||||||
|
{
|
||||||
|
case HANDSHAKING:
|
||||||
|
case IDLE:
|
||||||
|
{
|
||||||
|
if (netInput != null)
|
||||||
|
throw new IllegalStateException();
|
||||||
|
netInput = byteBufferPool.acquire(sslEngine.getSession().getPacketBufferSize(), direct);
|
||||||
|
appInput = byteBufferPool.acquire(sslEngine.getSession().getApplicationBufferSize(), false);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case UNDERFLOW:
|
||||||
|
{
|
||||||
|
if (netInput == null)
|
||||||
|
throw new IllegalStateException();
|
||||||
|
BufferUtil.compact(netInput);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
throw new IllegalStateException("Unexpected read state " + readState);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AsyncEndPoint endPoint = getEndPoint();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
while (true) // TODO: writes can close the connection, check that also ?
|
||||||
|
{
|
||||||
|
BufferUtil.compact(netInput);
|
||||||
|
int filled = endPoint.fill(netInput);
|
||||||
|
if (filled == 0)
|
||||||
|
{
|
||||||
|
scheduleOnReadable();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if (filled < 0)
|
||||||
|
{
|
||||||
|
updateReadState(ReadState.CLOSED);
|
||||||
|
sslEngine.closeInbound();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if (filled > 0)
|
||||||
|
{
|
||||||
|
boolean readMore = decrypt();
|
||||||
|
if (!readMore)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (IOException x)
|
||||||
|
{
|
||||||
|
endPoint.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean decrypt() throws SSLException
|
||||||
|
{
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
updateReadState(sslMachine.decrypt(netInput, appInput));
|
||||||
|
switch (readState)
|
||||||
|
{
|
||||||
|
case UNDERFLOW:
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case HANDSHAKEN:
|
||||||
|
{
|
||||||
|
getAppEndPoint().getAsyncConnection().onOpen();
|
||||||
|
if (!netInput.hasRemaining())
|
||||||
|
{
|
||||||
|
updateReadState(ReadState.IDLE);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case DECRYPTED:
|
||||||
|
{
|
||||||
|
appReader.completed(readContext);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
case CLOSED:
|
||||||
|
{
|
||||||
|
appReader.completed(readContext);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
throw new IllegalStateException("Unexpected read state " + readState);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAllowRenegotiate(boolean allowRenegotiate)
|
||||||
|
{
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ApplicationEndPoint extends AbstractEndPoint implements AsyncEndPoint
|
||||||
|
{
|
||||||
|
private final AtomicBoolean writing = new AtomicBoolean();
|
||||||
|
private boolean oshut;
|
||||||
|
private Object context;
|
||||||
|
private Callback callback;
|
||||||
|
private ByteBuffer[] buffers;
|
||||||
|
private AsyncConnection connection;
|
||||||
|
|
||||||
|
public ApplicationEndPoint()
|
||||||
|
{
|
||||||
|
super(getEndPoint().getLocalAddress(), getEndPoint().getRemoteAddress());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <C> void readable(C context, Callback<C> callback) throws IllegalStateException
|
||||||
|
{
|
||||||
|
if (appReader != null)
|
||||||
|
throw new ReadPendingException();
|
||||||
|
|
||||||
|
switch (readState)
|
||||||
|
{
|
||||||
|
case IDLE:
|
||||||
|
{
|
||||||
|
if (BufferUtil.hasContent(netInput))
|
||||||
|
throw new IllegalStateException();
|
||||||
|
appReader = callback;
|
||||||
|
readContext = context;
|
||||||
|
scheduleOnReadable();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case UNDERFLOW:
|
||||||
|
{
|
||||||
|
if (!BufferUtil.hasContent(netInput))
|
||||||
|
throw new IllegalStateException();
|
||||||
|
appReader = callback;
|
||||||
|
readContext = context;
|
||||||
|
scheduleOnReadable();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case DECRYPTED:
|
||||||
|
{
|
||||||
|
if (!BufferUtil.hasContent(appInput))
|
||||||
|
throw new IllegalStateException();
|
||||||
|
callback.completed(context);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CLOSED:
|
||||||
|
{
|
||||||
|
if (BufferUtil.hasContent(appInput))
|
||||||
|
throw new IllegalStateException();
|
||||||
|
callback.completed(context);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
throw new IllegalStateException("Unexpected read state " + readState);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int fill(ByteBuffer buffer) throws IOException
|
||||||
|
{
|
||||||
|
switch (readState)
|
||||||
|
{
|
||||||
|
case IDLE:
|
||||||
|
case UNDERFLOW:
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
case DECRYPTED:
|
||||||
|
{
|
||||||
|
if (!BufferUtil.hasContent(appInput))
|
||||||
|
throw new IllegalStateException();
|
||||||
|
|
||||||
|
int filled = BufferUtil.append(appInput, buffer);
|
||||||
|
if (!BufferUtil.hasContent(appInput))
|
||||||
|
{
|
||||||
|
byteBufferPool.release(appInput);
|
||||||
|
appInput = null;
|
||||||
|
updateReadState(BufferUtil.hasContent(netInput) ? ReadState.UNDERFLOW :
|
||||||
|
sslMachine.isRemoteClosed() ? ReadState.CLOSED : ReadState.IDLE);
|
||||||
|
}
|
||||||
|
return filled;
|
||||||
|
}
|
||||||
|
case CLOSED:
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
throw new IllegalStateException("Unexpected read state " + readState);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void shutdownOutput()
|
||||||
|
{
|
||||||
|
oshut = true;
|
||||||
|
sslMachine.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isOutputShutdown()
|
||||||
|
{
|
||||||
|
return oshut;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close()
|
||||||
|
{
|
||||||
|
getEndPoint().close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isOpen()
|
||||||
|
{
|
||||||
|
return getEndPoint().isOpen();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getTransport()
|
||||||
|
{
|
||||||
|
return getEndPoint();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isInputShutdown()
|
||||||
|
{
|
||||||
|
return sslMachine.isRemoteClosed();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int flush(ByteBuffer... appOutputs) throws IOException
|
||||||
|
{
|
||||||
|
switch (writeState)
|
||||||
|
{
|
||||||
|
case HANDSHAKING:
|
||||||
|
{
|
||||||
|
if (netOutput != null)
|
||||||
|
throw new IllegalStateException();
|
||||||
|
netOutput = byteBufferPool.acquire(sslEngine.getSession().getPacketBufferSize(), direct);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
throw new IllegalStateException("Unexpected write state " + readState);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ByteBuffer appOutput = appOutputs[0];
|
||||||
|
if (!appOutput.hasRemaining() && appOutputs.length > 1)
|
||||||
|
{
|
||||||
|
for (int i = 1; i < appOutputs.length; ++i)
|
||||||
|
{
|
||||||
|
if (appOutputs[i].hasRemaining())
|
||||||
|
{
|
||||||
|
appOutput = appOutputs[i];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int remaining = appOutput.remaining();
|
||||||
|
sslMachine.encrypt(appOutput, netOutput);
|
||||||
|
int result = remaining - appOutput.remaining();
|
||||||
|
|
||||||
|
getEndPoint().write(null, new Callback<Object>()
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void completed(Object context)
|
||||||
|
{
|
||||||
|
completeWrite();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void failed(Object context, Throwable x)
|
||||||
|
{
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
}, netOutput);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <C> void write(C context, Callback<C> callback, ByteBuffer... buffers) throws IllegalStateException
|
||||||
|
{
|
||||||
|
if (!writing.compareAndSet(false, true))
|
||||||
|
throw new WritePendingException();
|
||||||
|
|
||||||
|
boolean writePending = false;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
flush(buffers);
|
||||||
|
|
||||||
|
for (ByteBuffer buffer : buffers)
|
||||||
|
{
|
||||||
|
if (buffer.hasRemaining())
|
||||||
|
{
|
||||||
|
this.context = context;
|
||||||
|
this.callback = callback;
|
||||||
|
this.buffers = buffers;
|
||||||
|
writePending = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
callback.completed(context);
|
||||||
|
}
|
||||||
|
catch (IOException x)
|
||||||
|
{
|
||||||
|
callback.failed(context, x);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
writing.set(writePending);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void completeWrite()
|
||||||
|
{
|
||||||
|
if (buffers == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
flush(buffers);
|
||||||
|
|
||||||
|
for (ByteBuffer buffer : buffers)
|
||||||
|
{
|
||||||
|
if (buffer.hasRemaining())
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
callback.completed(context);
|
||||||
|
}
|
||||||
|
catch (IOException x)
|
||||||
|
{
|
||||||
|
callback.failed(context, x);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
context = null;
|
||||||
|
callback = null;
|
||||||
|
buffers = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setCheckForIdle(boolean check)
|
||||||
|
{
|
||||||
|
getEndPoint().setCheckForIdle(check);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isCheckForIdle()
|
||||||
|
{
|
||||||
|
return getEndPoint().isCheckForIdle();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AsyncConnection getAsyncConnection()
|
||||||
|
{
|
||||||
|
return connection;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setAsyncConnection(AsyncConnection connection)
|
||||||
|
{
|
||||||
|
this.connection = connection;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ConnectionSSLMachine extends SSLMachine
|
||||||
|
{
|
||||||
|
private ConnectionSSLMachine(SSLEngine engine)
|
||||||
|
{
|
||||||
|
super(engine);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void writeForDecrypt(ByteBuffer appOutput)
|
||||||
|
{
|
||||||
|
AsyncEndPoint endPoint = getAppEndPoint();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
endPoint.flush(appOutput);
|
||||||
|
}
|
||||||
|
catch (IOException x)
|
||||||
|
{
|
||||||
|
endPoint.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -11,7 +11,7 @@
|
||||||
// You may elect to redistribute this code under either of these licenses.
|
// You may elect to redistribute this code under either of these licenses.
|
||||||
// ========================================================================
|
// ========================================================================
|
||||||
|
|
||||||
package org.eclipse.jetty.io;
|
package org.eclipse.jetty.io.ssl;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
|
@ -25,6 +25,12 @@ import javax.net.ssl.SSLEngineResult.HandshakeStatus;
|
||||||
import javax.net.ssl.SSLException;
|
import javax.net.ssl.SSLException;
|
||||||
import javax.net.ssl.SSLSession;
|
import javax.net.ssl.SSLSession;
|
||||||
|
|
||||||
|
import org.eclipse.jetty.io.AbstractAsyncConnection;
|
||||||
|
import org.eclipse.jetty.io.AbstractEndPoint;
|
||||||
|
import org.eclipse.jetty.io.AsyncConnection;
|
||||||
|
import org.eclipse.jetty.io.AsyncEndPoint;
|
||||||
|
import org.eclipse.jetty.io.EndPoint;
|
||||||
|
import org.eclipse.jetty.io.SelectChannelEndPoint;
|
||||||
import org.eclipse.jetty.util.BufferUtil;
|
import org.eclipse.jetty.util.BufferUtil;
|
||||||
import org.eclipse.jetty.util.Callback;
|
import org.eclipse.jetty.util.Callback;
|
||||||
import org.eclipse.jetty.util.log.Log;
|
import org.eclipse.jetty.util.log.Log;
|
||||||
|
@ -39,7 +45,7 @@ import org.eclipse.jetty.util.log.Logger;
|
||||||
* it's source/sink of encrypted data. It then provides {@link #getAppEndPoint()} to
|
* it's source/sink of encrypted data. It then provides {@link #getAppEndPoint()} to
|
||||||
* expose a source/sink of unencrypted data to another connection (eg HttpConnection).
|
* expose a source/sink of unencrypted data to another connection (eg HttpConnection).
|
||||||
*/
|
*/
|
||||||
public class SslConnection extends AbstractAsyncConnection
|
public class SslConnectionOld extends AbstractAsyncConnection
|
||||||
{
|
{
|
||||||
static final Logger LOG = Log.getLogger("org.eclipse.jetty.io.ssl");
|
static final Logger LOG = Log.getLogger("org.eclipse.jetty.io.ssl");
|
||||||
|
|
||||||
|
@ -102,16 +108,17 @@ public class SslConnection extends AbstractAsyncConnection
|
||||||
_outNet=BufferUtil.allocateDirect(packetSize);
|
_outNet=BufferUtil.allocateDirect(packetSize);
|
||||||
_inApp=BufferUtil.allocate(appSize);
|
_inApp=BufferUtil.allocate(appSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
public SslConnection(SSLEngine engine,AsyncEndPoint endp,Executor executor)
|
public SslConnectionOld(SSLEngine engine,AsyncEndPoint endp,Executor executor)
|
||||||
{
|
{
|
||||||
this(engine,endp,System.currentTimeMillis(),executor);
|
this(engine,endp,System.currentTimeMillis(),executor);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
public SslConnection(SSLEngine engine,AsyncEndPoint endp, long timeStamp,Executor executor)
|
public SslConnectionOld(SSLEngine engine,AsyncEndPoint endp, long timeStamp,Executor executor)
|
||||||
{
|
{
|
||||||
super(endp, executor);
|
super(endp, executor);
|
||||||
_engine=engine;
|
_engine=engine;
|
|
@ -0,0 +1,19 @@
|
||||||
|
// ========================================================================
|
||||||
|
// Copyright (c) 2012 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.
|
||||||
|
// ========================================================================
|
||||||
|
|
||||||
|
package org.eclipse.jetty.io.ssl;
|
||||||
|
|
||||||
|
enum WriteState
|
||||||
|
{
|
||||||
|
HANDSHAKING, HANDSHAKEN, IDLE, ENCRYPTED, CLOSED
|
||||||
|
}
|
|
@ -10,9 +10,9 @@ import javax.net.ssl.SSLEngineResult;
|
||||||
import javax.net.ssl.SSLEngineResult.HandshakeStatus;
|
import javax.net.ssl.SSLEngineResult.HandshakeStatus;
|
||||||
import javax.net.ssl.SSLSocket;
|
import javax.net.ssl.SSLSocket;
|
||||||
|
|
||||||
|
import org.eclipse.jetty.io.ssl.SslConnection;
|
||||||
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
|
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
|
||||||
import org.eclipse.jetty.util.ssl.SslContextFactory;
|
import org.eclipse.jetty.util.ssl.SslContextFactory;
|
||||||
import org.eclipse.jetty.util.thread.QueuedThreadPool;
|
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.BeforeClass;
|
import org.junit.BeforeClass;
|
||||||
import org.junit.Ignore;
|
import org.junit.Ignore;
|
||||||
|
@ -22,7 +22,8 @@ import org.junit.Test;
|
||||||
@Ignore
|
@Ignore
|
||||||
public class SelectChannelEndPointSslTest extends SelectChannelEndPointTest
|
public class SelectChannelEndPointSslTest extends SelectChannelEndPointTest
|
||||||
{
|
{
|
||||||
static SslContextFactory __sslCtxFactory=new SslContextFactory();
|
private static SslContextFactory __sslCtxFactory=new SslContextFactory();
|
||||||
|
private static ByteBufferPool __byteBufferPool = new StandardByteBufferPool();
|
||||||
|
|
||||||
@BeforeClass
|
@BeforeClass
|
||||||
public static void initSslEngine() throws Exception
|
public static void initSslEngine() throws Exception
|
||||||
|
@ -47,11 +48,11 @@ public class SelectChannelEndPointSslTest extends SelectChannelEndPointTest
|
||||||
{
|
{
|
||||||
SSLEngine engine = __sslCtxFactory.newSslEngine();
|
SSLEngine engine = __sslCtxFactory.newSslEngine();
|
||||||
engine.setUseClientMode(false);
|
engine.setUseClientMode(false);
|
||||||
SslConnection connection = new SslConnection(engine,endpoint,_threadPool);
|
SslConnection sslConnection = new SslConnection(__byteBufferPool, _threadPool, endpoint, engine);
|
||||||
|
|
||||||
AsyncConnection delegate = super.newConnection(channel,connection.getAppEndPoint());
|
AsyncConnection appConnection = super.newConnection(channel,sslConnection.getAppEndPoint());
|
||||||
connection.getAppEndPoint().setAsyncConnection(delegate);
|
sslConnection.getAppEndPoint().setAsyncConnection(appConnection);
|
||||||
return connection;
|
return sslConnection;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -172,8 +173,6 @@ public class SelectChannelEndPointSslTest extends SelectChannelEndPointTest
|
||||||
|
|
||||||
Assert.assertEquals("HelloWorld",reply);
|
Assert.assertEquals("HelloWorld",reply);
|
||||||
|
|
||||||
SslConnection.LOG.info("javax.net.ssl.SSLException: Inbound closed before... Expected as next line!");
|
|
||||||
if (debug) System.err.println("\nSudden Death");
|
|
||||||
client.socket().shutdownOutput();
|
client.socket().shutdownOutput();
|
||||||
|
|
||||||
filled=client.read(sslIn);
|
filled=client.read(sslIn);
|
||||||
|
|
|
@ -35,6 +35,7 @@ public class SelectChannelEndPointTest
|
||||||
protected volatile AsyncEndPoint _lastEndp;
|
protected volatile AsyncEndPoint _lastEndp;
|
||||||
protected ServerSocketChannel _connector;
|
protected ServerSocketChannel _connector;
|
||||||
protected QueuedThreadPool _threadPool = new QueuedThreadPool();
|
protected QueuedThreadPool _threadPool = new QueuedThreadPool();
|
||||||
|
private int maxIdleTimeout = 600000; // TODO: use smaller value
|
||||||
protected SelectorManager _manager = new SelectorManager()
|
protected SelectorManager _manager = new SelectorManager()
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
|
@ -51,6 +52,7 @@ public class SelectChannelEndPointTest
|
||||||
@Override
|
@Override
|
||||||
protected void endPointOpened(AsyncEndPoint endpoint)
|
protected void endPointOpened(AsyncEndPoint endpoint)
|
||||||
{
|
{
|
||||||
|
endpoint.getAsyncConnection().onOpen();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -67,7 +69,7 @@ public class SelectChannelEndPointTest
|
||||||
@Override
|
@Override
|
||||||
protected SelectChannelEndPoint newEndPoint(SocketChannel channel, SelectSet selectSet, SelectionKey key) throws IOException
|
protected SelectChannelEndPoint newEndPoint(SocketChannel channel, SelectSet selectSet, SelectionKey key) throws IOException
|
||||||
{
|
{
|
||||||
SelectChannelEndPoint endp = new SelectChannelEndPoint(channel,selectSet,key,2000);
|
SelectChannelEndPoint endp = new SelectChannelEndPoint(channel,selectSet,key,maxIdleTimeout);
|
||||||
endp.setAsyncConnection(selectSet.getManager().newConnection(channel,endp, key.attachment()));
|
endp.setAsyncConnection(selectSet.getManager().newConnection(channel,endp, key.attachment()));
|
||||||
_lastEndp=endp;
|
_lastEndp=endp;
|
||||||
return endp;
|
return endp;
|
||||||
|
@ -104,9 +106,7 @@ public class SelectChannelEndPointTest
|
||||||
|
|
||||||
protected AsyncConnection newConnection(SocketChannel channel, AsyncEndPoint endpoint)
|
protected AsyncConnection newConnection(SocketChannel channel, AsyncEndPoint endpoint)
|
||||||
{
|
{
|
||||||
AbstractAsyncConnection connection = new TestConnection(endpoint);
|
return new TestConnection(endpoint);
|
||||||
connection.scheduleOnReadable();
|
|
||||||
return connection;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class TestConnection extends AbstractAsyncConnection
|
public class TestConnection extends AbstractAsyncConnection
|
||||||
|
@ -120,6 +120,12 @@ public class SelectChannelEndPointTest
|
||||||
super(endp,_threadPool);
|
super(endp,_threadPool);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onOpen()
|
||||||
|
{
|
||||||
|
scheduleOnReadable();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public synchronized void onReadable()
|
public synchronized void onReadable()
|
||||||
{
|
{
|
||||||
|
@ -233,7 +239,7 @@ public class SelectChannelEndPointTest
|
||||||
{
|
{
|
||||||
Socket client = newClient();
|
Socket client = newClient();
|
||||||
|
|
||||||
client.setSoTimeout(60000);
|
client.setSoTimeout(600000); // TODO: restore to smaller value
|
||||||
|
|
||||||
SocketChannel server = _connector.accept();
|
SocketChannel server = _connector.accept();
|
||||||
server.configureBlocking(false);
|
server.configureBlocking(false);
|
||||||
|
@ -356,7 +362,7 @@ public class SelectChannelEndPointTest
|
||||||
OutputStream clientOutputStream = client.getOutputStream();
|
OutputStream clientOutputStream = client.getOutputStream();
|
||||||
InputStream clientInputStream = client.getInputStream();
|
InputStream clientInputStream = client.getInputStream();
|
||||||
|
|
||||||
int specifiedTimeout = SslConnection.LOG.isDebugEnabled()?2000:400;
|
int specifiedTimeout = 2000;
|
||||||
client.setSoTimeout(specifiedTimeout);
|
client.setSoTimeout(specifiedTimeout);
|
||||||
|
|
||||||
// Write 8 and cause block waiting for 10
|
// Write 8 and cause block waiting for 10
|
||||||
|
|
|
@ -3,13 +3,11 @@ package org.eclipse.jetty.server.ssl;
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.security.cert.X509Certificate;
|
import java.security.cert.X509Certificate;
|
||||||
|
import javax.net.ssl.SSLEngine;
|
||||||
import javax.net.ssl.SSLPeerUnverifiedException;
|
import javax.net.ssl.SSLPeerUnverifiedException;
|
||||||
import javax.net.ssl.SSLSession;
|
import javax.net.ssl.SSLSession;
|
||||||
import javax.net.ssl.SSLSocket;
|
|
||||||
|
|
||||||
import org.eclipse.jetty.http.HttpScheme;
|
import org.eclipse.jetty.http.HttpScheme;
|
||||||
import org.eclipse.jetty.io.EndPoint;
|
|
||||||
import org.eclipse.jetty.server.Request;
|
import org.eclipse.jetty.server.Request;
|
||||||
import org.eclipse.jetty.util.TypeUtil;
|
import org.eclipse.jetty.util.TypeUtil;
|
||||||
import org.eclipse.jetty.util.log.Log;
|
import org.eclipse.jetty.util.log.Log;
|
||||||
|
@ -55,7 +53,7 @@ public class SslCertificates
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
/**
|
/**
|
||||||
|
@ -76,16 +74,14 @@ public class SslCertificates
|
||||||
* client, the next is the one used to authenticate the first, and so on.
|
* client, the next is the one used to authenticate the first, and so on.
|
||||||
* </li>
|
* </li>
|
||||||
* </ul>
|
* </ul>
|
||||||
*
|
*
|
||||||
* @param endpoint
|
|
||||||
* The Socket the request arrived on. This should be a
|
|
||||||
* {@link SocketEndPoint} wrapping a {@link SSLSocket}.
|
|
||||||
* @param request
|
* @param request
|
||||||
* HttpRequest to be customised.
|
* HttpRequest to be customised.
|
||||||
*/
|
*/
|
||||||
public static void customize(SSLSession sslSession, EndPoint endpoint, Request request) throws IOException
|
public static void customize(SSLEngine sslEngine, Request request) throws IOException
|
||||||
{
|
{
|
||||||
request.setScheme(HttpScheme.HTTPS.asString());
|
request.setScheme(HttpScheme.HTTPS.asString());
|
||||||
|
SSLSession sslSession = sslEngine.getSession();
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
@ -153,7 +149,7 @@ public class SslCertificates
|
||||||
{
|
{
|
||||||
return _keySize;
|
return _keySize;
|
||||||
}
|
}
|
||||||
|
|
||||||
String getIdStr()
|
String getIdStr()
|
||||||
{
|
{
|
||||||
return _idStr;
|
return _idStr;
|
||||||
|
|
|
@ -15,18 +15,15 @@ package org.eclipse.jetty.server.ssl;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.channels.SocketChannel;
|
import java.nio.channels.SocketChannel;
|
||||||
|
|
||||||
import javax.net.ssl.SSLContext;
|
import javax.net.ssl.SSLContext;
|
||||||
import javax.net.ssl.SSLEngine;
|
import javax.net.ssl.SSLEngine;
|
||||||
import javax.net.ssl.SSLSession;
|
import javax.net.ssl.SSLSession;
|
||||||
import javax.net.ssl.SSLSocket;
|
|
||||||
|
|
||||||
import org.eclipse.jetty.http.HttpScheme;
|
import org.eclipse.jetty.http.HttpScheme;
|
||||||
import org.eclipse.jetty.io.AsyncConnection;
|
import org.eclipse.jetty.io.AsyncConnection;
|
||||||
import org.eclipse.jetty.io.AsyncEndPoint;
|
import org.eclipse.jetty.io.AsyncEndPoint;
|
||||||
import org.eclipse.jetty.io.EndPoint;
|
|
||||||
import org.eclipse.jetty.io.RuntimeIOException;
|
import org.eclipse.jetty.io.RuntimeIOException;
|
||||||
import org.eclipse.jetty.io.SslConnection;
|
import org.eclipse.jetty.io.ssl.SslConnection;
|
||||||
import org.eclipse.jetty.server.Request;
|
import org.eclipse.jetty.server.Request;
|
||||||
import org.eclipse.jetty.server.nio.SelectChannelConnector;
|
import org.eclipse.jetty.server.nio.SelectChannelConnector;
|
||||||
import org.eclipse.jetty.util.component.AggregateLifeCycle;
|
import org.eclipse.jetty.util.component.AggregateLifeCycle;
|
||||||
|
@ -51,7 +48,7 @@ public class SslSelectChannelConnector extends SelectChannelConnector implements
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
/** Construct with explicit SslContextFactory.
|
/** Construct with explicit SslContextFactory.
|
||||||
* The SslContextFactory passed is added via {@link #addBean(Object)} so that
|
* The SslContextFactory passed is added via {@link #addBean(Object)} so that
|
||||||
* it's lifecycle may be managed with {@link AggregateLifeCycle}.
|
* it's lifecycle may be managed with {@link AggregateLifeCycle}.
|
||||||
* @param sslContextFactory
|
* @param sslContextFactory
|
||||||
*/
|
*/
|
||||||
|
@ -82,9 +79,6 @@ public class SslSelectChannelConnector extends SelectChannelConnector implements
|
||||||
* </li>
|
* </li>
|
||||||
* </ul>
|
* </ul>
|
||||||
*
|
*
|
||||||
* @param endpoint
|
|
||||||
* The Socket the request arrived on. This should be a
|
|
||||||
* {@link SocketEndPoint} wrapping a {@link SSLSocket}.
|
|
||||||
* @param request
|
* @param request
|
||||||
* HttpRequest to be customised.
|
* HttpRequest to be customised.
|
||||||
*/
|
*/
|
||||||
|
@ -94,12 +88,9 @@ public class SslSelectChannelConnector extends SelectChannelConnector implements
|
||||||
request.setScheme(HttpScheme.HTTPS.asString());
|
request.setScheme(HttpScheme.HTTPS.asString());
|
||||||
super.customize(request);
|
super.customize(request);
|
||||||
|
|
||||||
EndPoint endpoint = request.getHttpChannel().getConnection().getEndPoint();
|
SslConnection sslConnection = (SslConnection)request.getHttpChannel().getConnection();
|
||||||
SslConnection.AppEndPoint sslEndpoint=(SslConnection.AppEndPoint)endpoint;
|
SSLEngine sslEngine=sslConnection.getSSLEngine();
|
||||||
SSLEngine sslEngine=sslEndpoint.getSslEngine();
|
SslCertificates.customize(sslEngine,request);
|
||||||
SSLSession sslSession=sslEngine.getSession();
|
|
||||||
|
|
||||||
SslCertificates.customize(sslSession,endpoint,request);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
|
@ -563,7 +554,7 @@ public class SslSelectChannelConnector extends SelectChannelConnector implements
|
||||||
|
|
||||||
protected SslConnection newSslConnection(AsyncEndPoint endpoint, SSLEngine engine)
|
protected SslConnection newSslConnection(AsyncEndPoint endpoint, SSLEngine engine)
|
||||||
{
|
{
|
||||||
return new SslConnection(engine, endpoint,findExecutor());
|
return new SslConnection(getByteBufferPool(), findExecutor(), endpoint, engine);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
|
@ -624,5 +615,4 @@ public class SslSelectChannelConnector extends SelectChannelConnector implements
|
||||||
{
|
{
|
||||||
super.doStop();
|
super.doStop();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,10 +13,6 @@
|
||||||
|
|
||||||
package org.eclipse.jetty.server;
|
package org.eclipse.jetty.server;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
|
||||||
import static org.junit.Assert.assertFalse;
|
|
||||||
import static org.junit.matchers.JUnitMatchers.containsString;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
|
@ -24,19 +20,22 @@ import java.net.Socket;
|
||||||
import java.net.SocketException;
|
import java.net.SocketException;
|
||||||
import java.util.concurrent.Exchanger;
|
import java.util.concurrent.Exchanger;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import javax.net.ssl.SSLException;
|
import javax.net.ssl.SSLException;
|
||||||
import javax.servlet.ServletException;
|
import javax.servlet.ServletException;
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
import org.eclipse.jetty.io.EndPoint;
|
import org.eclipse.jetty.io.EndPoint;
|
||||||
import org.eclipse.jetty.io.SslConnection;
|
import org.eclipse.jetty.io.ssl.SslConnection;
|
||||||
import org.eclipse.jetty.server.handler.AbstractHandler;
|
import org.eclipse.jetty.server.handler.AbstractHandler;
|
||||||
import org.eclipse.jetty.util.IO;
|
import org.eclipse.jetty.util.IO;
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertFalse;
|
||||||
|
import static org.junit.matchers.JUnitMatchers.containsString;
|
||||||
|
|
||||||
public abstract class ConnectorTimeoutTest extends HttpServerTestFixture
|
public abstract class ConnectorTimeoutTest extends HttpServerTestFixture
|
||||||
{
|
{
|
||||||
protected static final int MAX_IDLE_TIME=250;
|
protected static final int MAX_IDLE_TIME=250;
|
||||||
|
@ -149,8 +148,8 @@ public abstract class ConnectorTimeoutTest extends HttpServerTestFixture
|
||||||
|
|
||||||
// Get the server side endpoint
|
// Get the server side endpoint
|
||||||
EndPoint endp = endpoint.exchange(null,10,TimeUnit.SECONDS);
|
EndPoint endp = endpoint.exchange(null,10,TimeUnit.SECONDS);
|
||||||
if (endp instanceof SslConnection.AppEndPoint)
|
if (endp instanceof SslConnection.ApplicationEndPoint)
|
||||||
endp=((SslConnection.AppEndPoint)endp).getEndpoint();
|
endp=((SslConnection.ApplicationEndPoint)endp).getEndpoint();
|
||||||
|
|
||||||
// read the response
|
// read the response
|
||||||
String result=IO.toString(is);
|
String result=IO.toString(is);
|
||||||
|
|
|
@ -53,15 +53,29 @@ public class SPDYAsyncConnection extends AbstractAsyncConnection implements Cont
|
||||||
BufferUtil.clear(buffer);
|
BufferUtil.clear(buffer);
|
||||||
read(buffer);
|
read(buffer);
|
||||||
bufferPool.release(buffer);
|
bufferPool.release(buffer);
|
||||||
|
scheduleOnReadable();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void read(ByteBuffer buffer)
|
protected void read(ByteBuffer buffer)
|
||||||
{
|
{
|
||||||
AsyncEndPoint endPoint = getEndPoint();
|
AsyncEndPoint endPoint = getEndPoint();
|
||||||
int filled = fill(endPoint, buffer);
|
while (true)
|
||||||
if (filled < 0)
|
{
|
||||||
close(false);
|
int filled = fill(endPoint, buffer);
|
||||||
parser.parse(buffer);
|
if (filled == 0)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if (filled < 0)
|
||||||
|
{
|
||||||
|
close(false);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
parser.parse(buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private int fill(AsyncEndPoint endPoint, ByteBuffer buffer)
|
private int fill(AsyncEndPoint endPoint, ByteBuffer buffer)
|
||||||
|
|
|
@ -47,8 +47,8 @@ import org.eclipse.jetty.io.AsyncEndPoint;
|
||||||
import org.eclipse.jetty.io.ByteBufferPool;
|
import org.eclipse.jetty.io.ByteBufferPool;
|
||||||
import org.eclipse.jetty.io.SelectChannelEndPoint;
|
import org.eclipse.jetty.io.SelectChannelEndPoint;
|
||||||
import org.eclipse.jetty.io.SelectorManager;
|
import org.eclipse.jetty.io.SelectorManager;
|
||||||
import org.eclipse.jetty.io.SslConnection;
|
|
||||||
import org.eclipse.jetty.io.StandardByteBufferPool;
|
import org.eclipse.jetty.io.StandardByteBufferPool;
|
||||||
|
import org.eclipse.jetty.io.ssl.SslConnection;
|
||||||
import org.eclipse.jetty.npn.NextProtoNego;
|
import org.eclipse.jetty.npn.NextProtoNego;
|
||||||
import org.eclipse.jetty.spdy.api.Session;
|
import org.eclipse.jetty.spdy.api.Session;
|
||||||
import org.eclipse.jetty.spdy.api.SessionFrameListener;
|
import org.eclipse.jetty.spdy.api.SessionFrameListener;
|
||||||
|
|
|
@ -36,8 +36,8 @@ import javax.net.ssl.SSLException;
|
||||||
import org.eclipse.jetty.io.AsyncConnection;
|
import org.eclipse.jetty.io.AsyncConnection;
|
||||||
import org.eclipse.jetty.io.AsyncEndPoint;
|
import org.eclipse.jetty.io.AsyncEndPoint;
|
||||||
import org.eclipse.jetty.io.ByteBufferPool;
|
import org.eclipse.jetty.io.ByteBufferPool;
|
||||||
import org.eclipse.jetty.io.SslConnection;
|
|
||||||
import org.eclipse.jetty.io.StandardByteBufferPool;
|
import org.eclipse.jetty.io.StandardByteBufferPool;
|
||||||
|
import org.eclipse.jetty.io.ssl.SslConnection;
|
||||||
import org.eclipse.jetty.npn.NextProtoNego;
|
import org.eclipse.jetty.npn.NextProtoNego;
|
||||||
import org.eclipse.jetty.server.nio.SelectChannelConnector;
|
import org.eclipse.jetty.server.nio.SelectChannelConnector;
|
||||||
import org.eclipse.jetty.spdy.api.SPDY;
|
import org.eclipse.jetty.spdy.api.SPDY;
|
||||||
|
|
Loading…
Reference in New Issue