INT-4198: TCP: Add Hook to Customize SSLEngine
JIRA: https://jira.spring.io/browse/INT-4198 Enable setting properties like `needClientAuth` on the `SSLEngine` - when not using NIO, this can be set on the server socket with a socket support implementation. Add `nio-connection-support` to namespace. Improved "Advanced Techniques" documentation, using this use case as an example. Fail fast with NIO when SSL handshaking fails. Polishing - PR Comments More Polishing * Final polishing - fix several typos in log messages - clean up `TcpConnectionFactoryFactoryBean` JavaDocs from redundant imports - remove redundant `InitializationBean` functionality from the `DefaultTcpNetSSLSocketFactorySupport` as well
This commit is contained in:
committed by
Artem Bilan
parent
bdab0aa1d3
commit
a0f0b6ab64
@@ -118,6 +118,8 @@ public abstract class IpAdapterParserUtils {
|
||||
|
||||
public static final String SOCKET_SUPPORT = "socket-support";
|
||||
|
||||
public static final String NIO_CONNECTION_SUPPORT = "nio-connection-support";
|
||||
|
||||
public static final String SOCKET_FACTORY_SUPPORT = "socket-factory-support";
|
||||
|
||||
public static final String BACKLOG = "backlog";
|
||||
|
||||
@@ -228,14 +228,7 @@ public class TcpConnectionFactoryFactoryBean extends AbstractFactoryBean<Abstrac
|
||||
return new DefaultTcpNetSocketFactorySupport();
|
||||
}
|
||||
else {
|
||||
DefaultTcpNetSSLSocketFactorySupport socketFactorySupport = new DefaultTcpNetSSLSocketFactorySupport(this.sslContextSupport);
|
||||
try {
|
||||
socketFactorySupport.afterPropertiesSet();
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new IllegalStateException("Failed to set up TcpSocketFactorySupport", e);
|
||||
}
|
||||
return socketFactorySupport;
|
||||
return new DefaultTcpNetSSLSocketFactorySupport(this.sslContextSupport);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -247,16 +240,8 @@ public class TcpConnectionFactoryFactoryBean extends AbstractFactoryBean<Abstrac
|
||||
return new DefaultTcpNioConnectionSupport();
|
||||
}
|
||||
else {
|
||||
DefaultTcpNioSSLConnectionSupport connectionSupport = new DefaultTcpNioSSLConnectionSupport(this.sslContextSupport);
|
||||
try {
|
||||
connectionSupport.afterPropertiesSet();
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new IllegalStateException("Failed to set up TcpConnectionSupport", e);
|
||||
}
|
||||
return connectionSupport;
|
||||
return new DefaultTcpNioSSLConnectionSupport(this.sslContextSupport);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -283,8 +268,8 @@ public class TcpConnectionFactoryFactoryBean extends AbstractFactoryBean<Abstrac
|
||||
}
|
||||
|
||||
/**
|
||||
* @param localAddress The local addres..
|
||||
* @see org.springframework.integration.ip.tcp.connection.AbstractServerConnectionFactory#setLocalAddress(java.lang.String)
|
||||
* @param localAddress The local address.
|
||||
* @see AbstractServerConnectionFactory#setLocalAddress(java.lang.String)
|
||||
*/
|
||||
public void setLocalAddress(String localAddress) {
|
||||
Assert.notNull(localAddress, "LocalAddress may not be null");
|
||||
@@ -293,7 +278,7 @@ public class TcpConnectionFactoryFactoryBean extends AbstractFactoryBean<Abstrac
|
||||
|
||||
/**
|
||||
* @param soTimeout The timeout.
|
||||
* @see org.springframework.integration.ip.tcp.connection.AbstractConnectionFactory#setSoTimeout(int)
|
||||
* @see AbstractConnectionFactory#setSoTimeout(int)
|
||||
*/
|
||||
public void setSoTimeout(int soTimeout) {
|
||||
this.soTimeout = soTimeout;
|
||||
@@ -301,7 +286,7 @@ public class TcpConnectionFactoryFactoryBean extends AbstractFactoryBean<Abstrac
|
||||
|
||||
/**
|
||||
* @param soReceiveBufferSize The receive buffer size.
|
||||
* @see org.springframework.integration.ip.tcp.connection.AbstractConnectionFactory#setSoReceiveBufferSize(int)
|
||||
* @see AbstractConnectionFactory#setSoReceiveBufferSize(int)
|
||||
*/
|
||||
public void setSoReceiveBufferSize(int soReceiveBufferSize) {
|
||||
this.soReceiveBufferSize = soReceiveBufferSize;
|
||||
@@ -309,7 +294,7 @@ public class TcpConnectionFactoryFactoryBean extends AbstractFactoryBean<Abstrac
|
||||
|
||||
/**
|
||||
* @param soSendBufferSize The send buffer size.
|
||||
* @see org.springframework.integration.ip.tcp.connection.AbstractConnectionFactory#setSoSendBufferSize(int)
|
||||
* @see AbstractConnectionFactory#setSoSendBufferSize(int)
|
||||
*/
|
||||
public void setSoSendBufferSize(int soSendBufferSize) {
|
||||
this.soSendBufferSize = soSendBufferSize;
|
||||
@@ -317,7 +302,7 @@ public class TcpConnectionFactoryFactoryBean extends AbstractFactoryBean<Abstrac
|
||||
|
||||
/**
|
||||
* @param soTcpNoDelay The TCP no delay to set.
|
||||
* @see org.springframework.integration.ip.tcp.connection.AbstractConnectionFactory#setSoTcpNoDelay(boolean)
|
||||
* @see AbstractConnectionFactory#setSoTcpNoDelay(boolean)
|
||||
*/
|
||||
public void setSoTcpNoDelay(boolean soTcpNoDelay) {
|
||||
this.soTcpNoDelay = soTcpNoDelay;
|
||||
@@ -325,7 +310,7 @@ public class TcpConnectionFactoryFactoryBean extends AbstractFactoryBean<Abstrac
|
||||
|
||||
/**
|
||||
* @param soLinger The SO Linger to set.
|
||||
* @see org.springframework.integration.ip.tcp.connection.AbstractConnectionFactory#setSoLinger(int)
|
||||
* @see AbstractConnectionFactory#setSoLinger(int)
|
||||
*/
|
||||
public void setSoLinger(int soLinger) {
|
||||
this.soLinger = soLinger;
|
||||
@@ -333,7 +318,7 @@ public class TcpConnectionFactoryFactoryBean extends AbstractFactoryBean<Abstrac
|
||||
|
||||
/**
|
||||
* @param soKeepAlive The SO keepalive to set.
|
||||
* @see org.springframework.integration.ip.tcp.connection.AbstractConnectionFactory#setSoKeepAlive(boolean)
|
||||
* @see AbstractConnectionFactory#setSoKeepAlive(boolean)
|
||||
*/
|
||||
public void setSoKeepAlive(boolean soKeepAlive) {
|
||||
this.soKeepAlive = soKeepAlive;
|
||||
@@ -341,7 +326,7 @@ public class TcpConnectionFactoryFactoryBean extends AbstractFactoryBean<Abstrac
|
||||
|
||||
/**
|
||||
* @param soTrafficClass The SO traffic class to set.
|
||||
* @see org.springframework.integration.ip.tcp.connection.AbstractConnectionFactory#setSoTrafficClass(int)
|
||||
* @see AbstractConnectionFactory#setSoTrafficClass(int)
|
||||
*/
|
||||
public void setSoTrafficClass(int soTrafficClass) {
|
||||
this.soTrafficClass = soTrafficClass;
|
||||
@@ -356,7 +341,7 @@ public class TcpConnectionFactoryFactoryBean extends AbstractFactoryBean<Abstrac
|
||||
|
||||
/**
|
||||
* @param usingDirectBuffers the usingDirectBuffers to set.
|
||||
* @see org.springframework.integration.ip.tcp.connection.TcpNioServerConnectionFactory#setUsingDirectBuffers(boolean)
|
||||
* @see TcpNioServerConnectionFactory#setUsingDirectBuffers(boolean)
|
||||
*/
|
||||
public void setUsingDirectBuffers(boolean usingDirectBuffers) {
|
||||
this.usingDirectBuffers = usingDirectBuffers;
|
||||
@@ -364,7 +349,7 @@ public class TcpConnectionFactoryFactoryBean extends AbstractFactoryBean<Abstrac
|
||||
|
||||
/**
|
||||
* @param taskExecutor The task executor.
|
||||
* @see org.springframework.integration.ip.tcp.connection.AbstractConnectionFactory#setTaskExecutor(java.util.concurrent.Executor)
|
||||
* @see AbstractConnectionFactory#setTaskExecutor(java.util.concurrent.Executor)
|
||||
*/
|
||||
public void setTaskExecutor(Executor taskExecutor) {
|
||||
Assert.notNull(taskExecutor, "Executor may not be null");
|
||||
@@ -373,7 +358,7 @@ public class TcpConnectionFactoryFactoryBean extends AbstractFactoryBean<Abstrac
|
||||
|
||||
/**
|
||||
* @param deserializer The deserializer.
|
||||
* @see org.springframework.integration.ip.tcp.connection.AbstractConnectionFactory#setDeserializer(org.springframework.core.serializer.Deserializer)
|
||||
* @see AbstractConnectionFactory#setDeserializer(org.springframework.core.serializer.Deserializer)
|
||||
*/
|
||||
public void setDeserializer(Deserializer<?> deserializer) {
|
||||
Assert.notNull(deserializer, "Deserializer may not be null");
|
||||
@@ -382,7 +367,7 @@ public class TcpConnectionFactoryFactoryBean extends AbstractFactoryBean<Abstrac
|
||||
|
||||
/**
|
||||
* @param serializer The serializer.
|
||||
* @see org.springframework.integration.ip.tcp.connection.AbstractConnectionFactory#setSerializer(org.springframework.core.serializer.Serializer)
|
||||
* @see AbstractConnectionFactory#setSerializer(org.springframework.core.serializer.Serializer)
|
||||
*/
|
||||
public void setSerializer(Serializer<?> serializer) {
|
||||
Assert.notNull(serializer, "Serializer may not be null");
|
||||
@@ -391,7 +376,7 @@ public class TcpConnectionFactoryFactoryBean extends AbstractFactoryBean<Abstrac
|
||||
|
||||
/**
|
||||
* @param mapper The mapper.
|
||||
* @see org.springframework.integration.ip.tcp.connection.AbstractConnectionFactory#setMapper(org.springframework.integration.ip.tcp.connection.TcpMessageMapper)
|
||||
* @see AbstractConnectionFactory#setMapper(TcpMessageMapper)
|
||||
*/
|
||||
public void setMapper(TcpMessageMapper mapper) {
|
||||
Assert.notNull(mapper, "TcpMessageMapper may not be null");
|
||||
@@ -401,7 +386,7 @@ public class TcpConnectionFactoryFactoryBean extends AbstractFactoryBean<Abstrac
|
||||
|
||||
/**
|
||||
* @param singleUse The singleUse to set.
|
||||
* @see org.springframework.integration.ip.tcp.connection.AbstractConnectionFactory#setSingleUse(boolean)
|
||||
* @see AbstractConnectionFactory#setSingleUse(boolean)
|
||||
*/
|
||||
public void setSingleUse(boolean singleUse) {
|
||||
this.singleUse = singleUse;
|
||||
@@ -417,7 +402,7 @@ public class TcpConnectionFactoryFactoryBean extends AbstractFactoryBean<Abstrac
|
||||
|
||||
/**
|
||||
* @param interceptorFactoryChain The interceptor factory chain.
|
||||
* @see org.springframework.integration.ip.tcp.connection.AbstractConnectionFactory#setInterceptorFactoryChain(org.springframework.integration.ip.tcp.connection.TcpConnectionInterceptorFactoryChain)
|
||||
* @see AbstractConnectionFactory#setInterceptorFactoryChain(TcpConnectionInterceptorFactoryChain)
|
||||
*/
|
||||
public void setInterceptorFactoryChain(
|
||||
TcpConnectionInterceptorFactoryChain interceptorFactoryChain) {
|
||||
@@ -427,7 +412,7 @@ public class TcpConnectionFactoryFactoryBean extends AbstractFactoryBean<Abstrac
|
||||
|
||||
/**
|
||||
* @param lookupHost The lookupHost to set.
|
||||
* @see org.springframework.integration.ip.tcp.connection.AbstractConnectionFactory#setLookupHost(boolean)
|
||||
* @see AbstractConnectionFactory#setLookupHost(boolean)
|
||||
*/
|
||||
public void setLookupHost(boolean lookupHost) {
|
||||
this.lookupHost = lookupHost;
|
||||
@@ -435,7 +420,7 @@ public class TcpConnectionFactoryFactoryBean extends AbstractFactoryBean<Abstrac
|
||||
|
||||
/**
|
||||
*
|
||||
* @see org.springframework.integration.ip.tcp.connection.AbstractConnectionFactory#start()
|
||||
* @see AbstractConnectionFactory#start()
|
||||
*/
|
||||
@Override
|
||||
public void start() {
|
||||
@@ -444,7 +429,7 @@ public class TcpConnectionFactoryFactoryBean extends AbstractFactoryBean<Abstrac
|
||||
|
||||
/**
|
||||
*
|
||||
* @see org.springframework.integration.ip.tcp.connection.AbstractConnectionFactory#stop()
|
||||
* @see AbstractConnectionFactory#stop()
|
||||
*/
|
||||
@Override
|
||||
public void stop() {
|
||||
@@ -473,7 +458,7 @@ public class TcpConnectionFactoryFactoryBean extends AbstractFactoryBean<Abstrac
|
||||
}
|
||||
|
||||
public void setSslContextSupport(TcpSSLContextSupport sslContextSupport) {
|
||||
Assert.notNull(sslContextSupport, "TcpSSLConstextSupport may not be null");
|
||||
Assert.notNull(sslContextSupport, "TcpSSLContextSupport may not be null");
|
||||
this.sslContextSupport = sslContextSupport;
|
||||
}
|
||||
|
||||
|
||||
@@ -90,6 +90,8 @@ public class TcpConnectionFactoryParser extends AbstractBeanDefinitionParser {
|
||||
IpAdapterParserUtils.SOCKET_FACTORY_SUPPORT);
|
||||
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element,
|
||||
IpAdapterParserUtils.SOCKET_SUPPORT);
|
||||
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element,
|
||||
IpAdapterParserUtils.NIO_CONNECTION_SUPPORT);
|
||||
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element,
|
||||
IpAdapterParserUtils.MAPPER);
|
||||
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element,
|
||||
|
||||
@@ -16,31 +16,36 @@
|
||||
|
||||
package org.springframework.integration.ip.tcp.connection;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.GeneralSecurityException;
|
||||
|
||||
import javax.net.ServerSocketFactory;
|
||||
import javax.net.SocketFactory;
|
||||
import javax.net.ssl.SSLContext;
|
||||
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Implementation of TcpSocketFactorySupport
|
||||
* for SSL sockets {@link javax.net.ssl.SSLServerSocket} and
|
||||
* {@link javax.net.ssl.SSLSocket}.
|
||||
* Implementation of {@link TcpSocketFactorySupport}
|
||||
* for SSL sockets {@link javax.net.ssl.SSLServerSocket} and {@link javax.net.ssl.SSLSocket}.
|
||||
*
|
||||
* @author Gary Russell
|
||||
* @since 2.2
|
||||
*
|
||||
*/
|
||||
public class DefaultTcpNetSSLSocketFactorySupport implements TcpSocketFactorySupport,
|
||||
InitializingBean {
|
||||
public class DefaultTcpNetSSLSocketFactorySupport implements TcpSocketFactorySupport {
|
||||
|
||||
private final TcpSSLContextSupport sslContextSupport;
|
||||
|
||||
private volatile SSLContext sslContext;
|
||||
private final SSLContext sslContext;
|
||||
|
||||
public DefaultTcpNetSSLSocketFactorySupport(TcpSSLContextSupport sslContextSupport) {
|
||||
Assert.notNull(sslContextSupport, "TcpSSLContextSupport must not be null");
|
||||
this.sslContextSupport = sslContextSupport;
|
||||
try {
|
||||
this.sslContext = sslContextSupport.getSSLContext();
|
||||
}
|
||||
catch (GeneralSecurityException | IOException e) {
|
||||
throw new IllegalArgumentException("Invalid TcpSSLContextSupport - it failed to provide an SSLContext", e);
|
||||
}
|
||||
Assert.notNull(this.sslContext, "SSLContext retrieved from context support must not be null");
|
||||
}
|
||||
|
||||
public ServerSocketFactory getServerSocketFactory() {
|
||||
@@ -51,9 +56,4 @@ public class DefaultTcpNetSSLSocketFactorySupport implements TcpSocketFactorySup
|
||||
return this.sslContext.getSocketFactory();
|
||||
}
|
||||
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
this.sslContext = this.sslContextSupport.getSSLContext();
|
||||
Assert.notNull(this.sslContext, "SSLContext must not be null");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -16,12 +16,13 @@
|
||||
|
||||
package org.springframework.integration.ip.tcp.connection;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.channels.SocketChannel;
|
||||
import java.security.GeneralSecurityException;
|
||||
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.SSLEngine;
|
||||
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
@@ -32,32 +33,42 @@ import org.springframework.util.Assert;
|
||||
* @since 2.2
|
||||
*
|
||||
*/
|
||||
public class DefaultTcpNioSSLConnectionSupport implements TcpNioConnectionSupport, InitializingBean {
|
||||
public class DefaultTcpNioSSLConnectionSupport implements TcpNioConnectionSupport {
|
||||
|
||||
private volatile SSLContext sslContext;
|
||||
|
||||
private final TcpSSLContextSupport sslContextSupport;
|
||||
|
||||
public DefaultTcpNioSSLConnectionSupport(TcpSSLContextSupport sslContextSupport) {
|
||||
Assert.notNull(sslContextSupport, "TcpSSLContextSupport must not be null");
|
||||
this.sslContextSupport = sslContextSupport;
|
||||
try {
|
||||
this.sslContext = sslContextSupport.getSSLContext();
|
||||
}
|
||||
catch (GeneralSecurityException | IOException e) {
|
||||
throw new IllegalArgumentException("Invalid TcpSSLContextSupport - it failed to provide an SSLContext", e);
|
||||
}
|
||||
Assert.notNull(this.sslContext, "SSLContext retrieved from context support must not be null");
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link TcpNioSSLConnection}.
|
||||
*/
|
||||
@Override
|
||||
public TcpNioConnection createNewConnection(SocketChannel socketChannel, boolean server, boolean lookupHost,
|
||||
ApplicationEventPublisher applicationEventPublisher, String connectionFactoryName) throws Exception {
|
||||
SSLEngine sslEngine = this.sslContext.createSSLEngine();
|
||||
postProcessSSLEngine(sslEngine);
|
||||
TcpNioSSLConnection tcpNioSSLConnection = new TcpNioSSLConnection(socketChannel, server, lookupHost,
|
||||
applicationEventPublisher, connectionFactoryName, sslEngine);
|
||||
tcpNioSSLConnection.init();
|
||||
return tcpNioSSLConnection;
|
||||
}
|
||||
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
this.sslContext = this.sslContextSupport.getSSLContext();
|
||||
Assert.notNull(this.sslContext, "SSLContext must not be null");
|
||||
/**
|
||||
* Subclasses can post-process the ssl engine (set properties).
|
||||
* @param sslEngine the engine.
|
||||
* @since 4.3.7
|
||||
*/
|
||||
protected void postProcessSSLEngine(SSLEngine sslEngine) {
|
||||
// NOSONAR (empty)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -47,4 +47,5 @@ public interface TcpNioConnectionSupport {
|
||||
boolean server, boolean lookupHost,
|
||||
ApplicationEventPublisher applicationEventPublisher,
|
||||
String connectionFactoryName) throws Exception;
|
||||
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ import javax.net.ssl.SSLEngineResult;
|
||||
import javax.net.ssl.SSLEngineResult.HandshakeStatus;
|
||||
import javax.net.ssl.SSLEngineResult.Status;
|
||||
import javax.net.ssl.SSLException;
|
||||
import javax.net.ssl.SSLHandshakeException;
|
||||
import javax.net.ssl.SSLSession;
|
||||
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
@@ -73,6 +74,8 @@ public class TcpNioSSLConnection extends TcpNioConnection {
|
||||
|
||||
private boolean needMoreNetworkData;
|
||||
|
||||
private SSLHandshakeException sslFatal;
|
||||
|
||||
public TcpNioSSLConnection(SocketChannel socketChannel, boolean server, boolean lookupHost,
|
||||
ApplicationEventPublisher applicationEventPublisher, String connectionFactoryName,
|
||||
SSLEngine sslEngine) throws Exception {
|
||||
@@ -106,13 +109,20 @@ public class TcpNioSSLConnection extends TcpNioConnection {
|
||||
protected void sendToPipe(final ByteBuffer networkBuffer) throws IOException {
|
||||
Assert.notNull(networkBuffer, "rawBuffer cannot be null");
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("sendToPipe " + this.sslEngine.getHandshakeStatus() + ", remaining:" + networkBuffer.remaining());
|
||||
logger.debug("sendToPipe " + this.sslEngine.getHandshakeStatus() + ", remaining: " + networkBuffer.remaining());
|
||||
}
|
||||
SSLEngineResult result = null;
|
||||
while (!this.needMoreNetworkData) {
|
||||
result = decode(networkBuffer);
|
||||
try {
|
||||
result = decode(networkBuffer);
|
||||
}
|
||||
catch (SSLHandshakeException e) {
|
||||
this.sslFatal = e;
|
||||
this.semaphore.release();
|
||||
throw e;
|
||||
}
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("result " + resultToString(result) + ", remaining:" + networkBuffer.remaining());
|
||||
logger.debug("result " + resultToString(result) + ", remaining: " + networkBuffer.remaining());
|
||||
}
|
||||
}
|
||||
this.needMoreNetworkData = false;
|
||||
@@ -123,7 +133,7 @@ public class TcpNioSSLConnection extends TcpNioConnection {
|
||||
networkBuffer.clear();
|
||||
}
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("sendToPipe.x " + resultToString(result) + ", remaining:" + networkBuffer.remaining());
|
||||
logger.debug("sendToPipe.x " + resultToString(result) + ", remaining: " + networkBuffer.remaining());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -148,7 +158,7 @@ public class TcpNioSSLConnection extends TcpNioConnection {
|
||||
this.decoded.clear();
|
||||
result = this.sslEngine.unwrap(networkBuffer, this.decoded);
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("After unwrap:" + resultToString(result));
|
||||
logger.debug("After unwrap: " + resultToString(result));
|
||||
}
|
||||
Status status = result.getStatus();
|
||||
if (status == Status.BUFFER_OVERFLOW) {
|
||||
@@ -164,7 +174,7 @@ public class TcpNioSSLConnection extends TcpNioConnection {
|
||||
this.encoded.clear();
|
||||
result = this.sslEngine.wrap(networkBuffer, this.encoded);
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("After wrap:" + resultToString(result));
|
||||
logger.debug("After wrap: " + resultToString(result));
|
||||
}
|
||||
if (result.getStatus() == Status.BUFFER_OVERFLOW) {
|
||||
this.encoded = this.allocateEncryptionBuffer(this.sslEngine.getSession().getPacketBufferSize());
|
||||
@@ -197,7 +207,7 @@ public class TcpNioSSLConnection extends TcpNioConnection {
|
||||
private boolean resumeWriterIfNeeded() {
|
||||
if (this.writerActive) {
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("Waking sender, permits:" + this.semaphore.availablePermits());
|
||||
logger.trace("Waking sender, permits: " + this.semaphore.availablePermits());
|
||||
}
|
||||
this.semaphore.release();
|
||||
return true;
|
||||
@@ -285,6 +295,12 @@ public class TcpNioSSLConnection extends TcpNioConnection {
|
||||
return result.toString().replace('\n', ' ');
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
super.close();
|
||||
this.semaphore.release();
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclass of {@link TcpNioConnection.ChannelOutputStream} to handle encryption
|
||||
* of outbound data. Wraps an instance of the superclass, which is invoked to
|
||||
@@ -340,8 +356,7 @@ public class TcpNioSSLConnection extends TcpNioConnection {
|
||||
* Handles SSL handshaking; when network data is needed from the peer, suspends
|
||||
* until that data is received.
|
||||
*/
|
||||
private void doClientSideHandshake(ByteBuffer plainText,
|
||||
SSLEngineResult result) throws IOException, SSLException {
|
||||
private void doClientSideHandshake(ByteBuffer plainText, SSLEngineResult result) throws IOException {
|
||||
TcpNioSSLConnection.this.semaphore.drainPermits();
|
||||
HandshakeStatus status = TcpNioSSLConnection.this.sslEngine.getHandshakeStatus();
|
||||
while (status != HandshakeStatus.FINISHED) {
|
||||
@@ -378,16 +393,20 @@ public class TcpNioSSLConnection extends TcpNioConnection {
|
||||
private HandshakeStatus waitForHandshakeData(SSLEngineResult result,
|
||||
HandshakeStatus status) throws IOException {
|
||||
try {
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("Writer waiting for handshake");
|
||||
}
|
||||
logger.trace("Writer waiting for handshake");
|
||||
if (!TcpNioSSLConnection.this.semaphore.tryAcquire(TcpNioSSLConnection.this.handshakeTimeout,
|
||||
TimeUnit.SECONDS)) {
|
||||
throw new MessagingException("SSL Handshaking taking too long");
|
||||
}
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("Writer resuming handshake");
|
||||
else {
|
||||
if (TcpNioSSLConnection.this.sslFatal != null) {
|
||||
throw TcpNioSSLConnection.this.sslFatal;
|
||||
}
|
||||
else if (!isOpen()) {
|
||||
throw new IOException("Socket closed during SSL Handshake");
|
||||
}
|
||||
}
|
||||
logger.trace("Writer resuming handshake");
|
||||
status = runTasksIfNeeded(result);
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
@@ -405,7 +424,9 @@ public class TcpNioSSLConnection extends TcpNioConnection {
|
||||
TcpNioSSLConnection.this.encoded.clear();
|
||||
SSLEngineResult result = TcpNioSSLConnection.this.sslEngine.wrap(plainText, TcpNioSSLConnection.this.encoded);
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("After wrap:" + resultToString(result) + " Plaintext buffer @" + plainText.position() + "/" + plainText.limit());
|
||||
logger.debug("After wrap: "
|
||||
+ resultToString(result)
|
||||
+ " Plaintext buffer @" + plainText.position() + "/" + plainText.limit());
|
||||
}
|
||||
if (result.getStatus() == SSLEngineResult.Status.BUFFER_OVERFLOW) {
|
||||
TcpNioSSLConnection.this.encoded = allocateEncryptionBuffer(TcpNioSSLConnection.this.sslEngine.getSession().getPacketBufferSize());
|
||||
@@ -420,5 +441,7 @@ public class TcpNioSSLConnection extends TcpNioConnection {
|
||||
void writeEncoded(ByteBuffer encoded) throws IOException {
|
||||
this.channelOutputStream.doWrite(encoded);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -172,15 +172,13 @@ public class TcpNioServerConnectionFactory extends AbstractServerConnectionFacto
|
||||
timeout = getReadDelay();
|
||||
}
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("Delayed reads:" + getDelayedReads().size() + " timeout " + timeout);
|
||||
logger.trace("Delayed reads: " + getDelayedReads().size() + " timeout " + timeout);
|
||||
}
|
||||
selectionCount = selector.select(timeout);
|
||||
processNioSelections(selectionCount, selector, server, this.channelMap);
|
||||
}
|
||||
catch (CancelledKeyException cke) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("CancelledKeyException during Selector.select()");
|
||||
}
|
||||
logger.debug("CancelledKeyException during Selector.select()");
|
||||
}
|
||||
catch (ClosedSelectorException cse) {
|
||||
if (isActive()) {
|
||||
|
||||
@@ -737,6 +737,23 @@
|
||||
</xsd:appinfo>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="nio-connection-support" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
A reference to a TcpNioConnectionSupport strategy implementation.
|
||||
When 'using-nio' is true, this is used to create connections.
|
||||
Two default implementations are provided 'DefaultTcpNioConnectionSupport'
|
||||
and 'DefaultTcpNioSSLConnectionSupport' depending on whether SSL is in
|
||||
use of not.
|
||||
</xsd:documentation>
|
||||
<xsd:appinfo>
|
||||
<tool:annotation kind="ref">
|
||||
<tool:expected-type
|
||||
type="org.springframework.integration.ip.tcp.connection.TcpNioConnectionSupport" />
|
||||
</tool:annotation>
|
||||
</xsd:appinfo>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="mapper" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
|
||||
@@ -72,6 +72,7 @@
|
||||
using-nio="true"
|
||||
ssl-context-support="sslContextSupport"
|
||||
ssl-handshake-timeout="43"
|
||||
nio-connection-support="nioConnectionSupport"
|
||||
/>
|
||||
|
||||
<bean id="sslContextSupport" class="org.springframework.integration.ip.tcp.connection.DefaultTcpSSLContextSupport">
|
||||
@@ -81,6 +82,9 @@
|
||||
<constructor-arg value="secret"/>
|
||||
</bean>
|
||||
|
||||
<bean id="nioConnectionSupport"
|
||||
class="org.springframework.integration.ip.tcp.connection.DefaultTcpNioSSLConnectionSupport" />
|
||||
|
||||
<ip:tcp-connection-factory id="secureServer"
|
||||
type="server"
|
||||
port="#{tcpIpUtils.findAvailableServerSocket(5250)}"
|
||||
|
||||
@@ -36,7 +36,6 @@ import org.springframework.beans.DirectFieldAccessor;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.core.io.UrlResource;
|
||||
import org.springframework.core.serializer.Deserializer;
|
||||
import org.springframework.core.serializer.Serializer;
|
||||
@@ -324,29 +323,26 @@ public class ParserUnitTests {
|
||||
assertEquals(124, tcpIn.getPhase());
|
||||
TcpMessageMapper cfS1Mapper = TestUtils.getPropertyValue(cfS1, "mapper", TcpMessageMapper.class);
|
||||
assertSame(mapper, cfS1Mapper);
|
||||
assertTrue((Boolean) TestUtils.getPropertyValue(cfS1Mapper, "applySequence"));
|
||||
assertTrue(TestUtils.getPropertyValue(cfS1Mapper, "applySequence", Boolean.class));
|
||||
Object socketSupport = TestUtils.getPropertyValue(cfS1, "tcpSocketFactorySupport");
|
||||
assertTrue(socketSupport instanceof DefaultTcpNetSSLSocketFactorySupport);
|
||||
assertNotNull(TestUtils.getPropertyValue(socketSupport, "sslContext"));
|
||||
|
||||
TcpSSLContextSupport contextSupport = TestUtils.getPropertyValue(cfS1, "tcpSocketFactorySupport.sslContextSupport", TcpSSLContextSupport.class);
|
||||
assertSame(contextSupport, this.contextSupport);
|
||||
assertTrue(TestUtils.getPropertyValue(contextSupport, "keyStore") instanceof ClassPathResource);
|
||||
assertTrue(TestUtils.getPropertyValue(contextSupport, "trustStore") instanceof ClassPathResource);
|
||||
|
||||
contextSupport = new DefaultTcpSSLContextSupport("http:foo", "file:bar", "", "");
|
||||
assertTrue(TestUtils.getPropertyValue(contextSupport, "keyStore") instanceof UrlResource);
|
||||
assertTrue(TestUtils.getPropertyValue(contextSupport, "trustStore") instanceof UrlResource);
|
||||
TcpSSLContextSupport tcpSSLContextSupport = new DefaultTcpSSLContextSupport("http:foo", "file:bar", "", "");
|
||||
assertTrue(TestUtils.getPropertyValue(tcpSSLContextSupport, "keyStore") instanceof UrlResource);
|
||||
assertTrue(TestUtils.getPropertyValue(tcpSSLContextSupport, "trustStore") instanceof UrlResource);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInTcpNioSSLDefaultConfig() {
|
||||
assertFalse(cfS1Nio.isLookupHost());
|
||||
assertTrue((Boolean) TestUtils.getPropertyValue(cfS1Nio, "mapper.applySequence"));
|
||||
assertTrue(TestUtils.getPropertyValue(cfS1Nio, "mapper.applySequence", Boolean.class));
|
||||
Object connectionSupport = TestUtils.getPropertyValue(cfS1Nio, "tcpNioConnectionSupport");
|
||||
assertTrue(connectionSupport instanceof DefaultTcpNioSSLConnectionSupport);
|
||||
assertNotNull(TestUtils.getPropertyValue(connectionSupport, "sslContext"));
|
||||
assertEquals(43, TestUtils.getPropertyValue(this.cfS1Nio, "sslHandshakeTimeout"));
|
||||
assertSame(this.ctx.getBean(DefaultTcpNioSSLConnectionSupport.class),
|
||||
TestUtils.getPropertyValue(this.cfS1Nio, "tcpNioConnectionSupport"));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -16,17 +16,23 @@
|
||||
|
||||
package org.springframework.integration.ip.tcp.connection;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.containsString;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.InetAddress;
|
||||
import java.net.ServerSocket;
|
||||
import java.net.Socket;
|
||||
import java.net.SocketException;
|
||||
import java.nio.channels.ClosedChannelException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
@@ -39,6 +45,8 @@ import java.util.concurrent.atomic.AtomicReference;
|
||||
import javax.net.ServerSocketFactory;
|
||||
import javax.net.SocketFactory;
|
||||
import javax.net.ssl.SSLEngine;
|
||||
import javax.net.ssl.SSLException;
|
||||
import javax.net.ssl.SSLServerSocket;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.mockito.Mockito;
|
||||
@@ -274,7 +282,6 @@ Certificate fingerprints:
|
||||
"test.truststore.ks", "secret", "secret");
|
||||
DefaultTcpNetSSLSocketFactorySupport tcpSocketFactorySupport =
|
||||
new DefaultTcpNetSSLSocketFactorySupport(sslContextSupport);
|
||||
tcpSocketFactorySupport.afterPropertiesSet();
|
||||
server.setTcpSocketFactorySupport(tcpSocketFactorySupport);
|
||||
final List<Message<?>> messages = new ArrayList<Message<?>>();
|
||||
final CountDownLatch latch = new CountDownLatch(1);
|
||||
@@ -303,13 +310,23 @@ Certificate fingerprints:
|
||||
|
||||
@Test
|
||||
public void testNetClientAndServerSSLDifferentContexts() throws Exception {
|
||||
testNetClientAndServerSSLDifferentContexts(false);
|
||||
try {
|
||||
testNetClientAndServerSSLDifferentContexts(true);
|
||||
fail("expected Exception");
|
||||
}
|
||||
catch (SSLException | SocketException e) {
|
||||
// NOSONAR
|
||||
}
|
||||
}
|
||||
|
||||
private void testNetClientAndServerSSLDifferentContexts(boolean badClient) throws Exception {
|
||||
System.setProperty("javax.net.debug", "all"); // SSL activity in the console
|
||||
TcpNetServerConnectionFactory server = new TcpNetServerConnectionFactory(0);
|
||||
TcpSSLContextSupport serverSslContextSupport = new DefaultTcpSSLContextSupport("server.ks",
|
||||
"server.truststore.ks", "secret", "secret");
|
||||
DefaultTcpNetSSLSocketFactorySupport serverTcpSocketFactorySupport =
|
||||
new DefaultTcpNetSSLSocketFactorySupport(serverSslContextSupport);
|
||||
serverTcpSocketFactorySupport.afterPropertiesSet();
|
||||
server.setTcpSocketFactorySupport(serverTcpSocketFactorySupport);
|
||||
final List<Message<?>> messages = new ArrayList<Message<?>>();
|
||||
final CountDownLatch latch = new CountDownLatch(1);
|
||||
@@ -318,15 +335,23 @@ Certificate fingerprints:
|
||||
latch.countDown();
|
||||
return false;
|
||||
});
|
||||
server.setTcpSocketSupport(new DefaultTcpSocketSupport() {
|
||||
|
||||
@Override
|
||||
public void postProcessServerSocket(ServerSocket serverSocket) {
|
||||
((SSLServerSocket) serverSocket).setNeedClientAuth(true);
|
||||
}
|
||||
|
||||
});
|
||||
server.start();
|
||||
TestingUtilities.waitListening(server, null);
|
||||
|
||||
TcpNetClientConnectionFactory client = new TcpNetClientConnectionFactory("localhost", server.getPort());
|
||||
TcpSSLContextSupport clientSslContextSupport = new DefaultTcpSSLContextSupport("client.ks",
|
||||
TcpSSLContextSupport clientSslContextSupport = new DefaultTcpSSLContextSupport(
|
||||
badClient ? "server.ks" : "client.ks",
|
||||
"client.truststore.ks", "secret", "secret");
|
||||
DefaultTcpNetSSLSocketFactorySupport clientTcpSocketFactorySupport =
|
||||
new DefaultTcpNetSSLSocketFactorySupport(clientSslContextSupport);
|
||||
clientTcpSocketFactorySupport.afterPropertiesSet();
|
||||
client.setTcpSocketFactorySupport(clientTcpSocketFactorySupport);
|
||||
client.start();
|
||||
|
||||
@@ -349,7 +374,6 @@ Certificate fingerprints:
|
||||
sslContextSupport.setProtocol("SSL");
|
||||
DefaultTcpNioSSLConnectionSupport tcpNioConnectionSupport =
|
||||
new DefaultTcpNioSSLConnectionSupport(sslContextSupport);
|
||||
tcpNioConnectionSupport.afterPropertiesSet();
|
||||
server.setTcpNioConnectionSupport(tcpNioConnectionSupport);
|
||||
final List<Message<?>> messages = new ArrayList<Message<?>>();
|
||||
final CountDownLatch latch = new CountDownLatch(1);
|
||||
@@ -391,6 +415,63 @@ Certificate fingerprints:
|
||||
server.stop();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNioClientAndServerSSLDifferentContexts() throws Exception {
|
||||
testNioClientAndServerSSLDifferentContexts(false);
|
||||
try {
|
||||
testNioClientAndServerSSLDifferentContexts(true);
|
||||
fail("expected Exception");
|
||||
}
|
||||
catch (IOException e) {
|
||||
if (!(e instanceof ClosedChannelException)) {
|
||||
assertThat(e.getMessage(), containsString("Socket closed during SSL Handshake"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void testNioClientAndServerSSLDifferentContexts(boolean badClient) throws Exception {
|
||||
System.setProperty("javax.net.debug", "all"); // SSL activity in the console
|
||||
TcpNioServerConnectionFactory server = new TcpNioServerConnectionFactory(0);
|
||||
TcpSSLContextSupport serverSslContextSupport = new DefaultTcpSSLContextSupport("server.ks",
|
||||
"server.truststore.ks", "secret", "secret");
|
||||
DefaultTcpNioSSLConnectionSupport tcpNioConnectionSupport =
|
||||
new DefaultTcpNioSSLConnectionSupport(serverSslContextSupport) {
|
||||
|
||||
@Override
|
||||
protected void postProcessSSLEngine(SSLEngine sslEngine) {
|
||||
sslEngine.setNeedClientAuth(true);
|
||||
}
|
||||
|
||||
};
|
||||
server.setTcpNioConnectionSupport(tcpNioConnectionSupport);
|
||||
final List<Message<?>> messages = new ArrayList<Message<?>>();
|
||||
final CountDownLatch latch = new CountDownLatch(1);
|
||||
server.registerListener(message -> {
|
||||
messages.add(message);
|
||||
latch.countDown();
|
||||
return false;
|
||||
});
|
||||
server.start();
|
||||
TestingUtilities.waitListening(server, null);
|
||||
|
||||
TcpNioClientConnectionFactory client = new TcpNioClientConnectionFactory("localhost", server.getPort());
|
||||
TcpSSLContextSupport clientSslContextSupport = new DefaultTcpSSLContextSupport(
|
||||
badClient ? "server.ks" : "client.ks",
|
||||
"client.truststore.ks", "secret", "secret");
|
||||
DefaultTcpNioSSLConnectionSupport clientTcpNioConnectionSupport =
|
||||
new DefaultTcpNioSSLConnectionSupport(clientSslContextSupport);
|
||||
client.setTcpNioConnectionSupport(clientTcpNioConnectionSupport);
|
||||
client.start();
|
||||
|
||||
TcpConnection connection = client.getConnection();
|
||||
connection.send(new GenericMessage<String>("Hello, world!"));
|
||||
assertTrue(latch.await(10, TimeUnit.SECONDS));
|
||||
assertEquals("Hello, world!", new String((byte[]) messages.get(0).getPayload()));
|
||||
|
||||
client.stop();
|
||||
server.stop();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNioClientAndServerSSLDifferentContextsLargeDataWithReply() throws Exception {
|
||||
System.setProperty("javax.net.debug", "all"); // SSL activity in the console
|
||||
@@ -399,7 +480,6 @@ Certificate fingerprints:
|
||||
"server.truststore.ks", "secret", "secret");
|
||||
DefaultTcpNioSSLConnectionSupport serverTcpNioConnectionSupport =
|
||||
new DefaultTcpNioSSLConnectionSupport(serverSslContextSupport);
|
||||
serverTcpNioConnectionSupport.afterPropertiesSet();
|
||||
server.setTcpNioConnectionSupport(serverTcpNioConnectionSupport);
|
||||
final List<Message<?>> messages = new ArrayList<Message<?>>();
|
||||
final CountDownLatch latch = new CountDownLatch(2);
|
||||
@@ -411,7 +491,7 @@ Certificate fingerprints:
|
||||
replier.send(message);
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
latch.countDown();
|
||||
return false;
|
||||
@@ -433,7 +513,6 @@ Certificate fingerprints:
|
||||
"client.truststore.ks", "secret", "secret");
|
||||
DefaultTcpNioSSLConnectionSupport clientTcpNioConnectionSupport =
|
||||
new DefaultTcpNioSSLConnectionSupport(clientSslContextSupport);
|
||||
clientTcpNioConnectionSupport.afterPropertiesSet();
|
||||
client.setTcpNioConnectionSupport(clientTcpNioConnectionSupport);
|
||||
client.registerListener(message -> {
|
||||
messages.add(message);
|
||||
|
||||
@@ -382,7 +382,7 @@ public class TcpNioConnectionReadTests {
|
||||
assertTrue(errorMessageLetch.await(10, TimeUnit.SECONDS));
|
||||
|
||||
assertThat(errorMessageRef.get().getMessage(),
|
||||
containsString("Connection is closed"));
|
||||
anyOf(containsString("Connection is closed"), containsString("Stream closed after 2 of 3")));
|
||||
|
||||
assertTrue(semaphore.tryAcquire(10000, TimeUnit.MILLISECONDS));
|
||||
assertTrue(removed.size() > 0);
|
||||
@@ -473,7 +473,6 @@ public class TcpNioConnectionReadTests {
|
||||
|
||||
private void testClosureMidMessageGuts(AbstractByteArraySerializer serializer, String shortMessage)
|
||||
throws Exception {
|
||||
final List<Message<?>> responses = new ArrayList<Message<?>>();
|
||||
final Semaphore semaphore = new Semaphore(0);
|
||||
final List<TcpConnection> added = new ArrayList<TcpConnection>();
|
||||
final List<TcpConnection> removed = new ArrayList<TcpConnection>();
|
||||
@@ -500,6 +499,7 @@ public class TcpNioConnectionReadTests {
|
||||
removed.add(connection);
|
||||
semaphore.release();
|
||||
}
|
||||
|
||||
});
|
||||
Socket socket = SocketFactory.getDefault().createSocket("localhost", scf.getPort());
|
||||
socket.getOutputStream().write(shortMessage.getBytes());
|
||||
|
||||
@@ -886,17 +886,20 @@ After establishing the key stores, the next step is to indicate their locations
|
||||
type="client"
|
||||
host="localhost"
|
||||
port="1234"
|
||||
ssl-context-support="sslContextSupport"
|
||||
ssl-context-support="sslContextSupport" />
|
||||
----
|
||||
|
||||
The `DefaulTcpSSLContextSupport` class also has an optional 'protocol' property, which can be 'SSL' or 'TLS' (default).
|
||||
The `DefaulTcpSSLContextSupport` class also has an optional `protocol` property, which can be `SSL` or `TLS` (default).
|
||||
|
||||
The keystore file names (first two constructor arguments) use the Spring `Resource` abstraction; by default the files will be located on the classpath, but this can be overridden by using the `file:` prefix, to find the files on the filesystem instead.
|
||||
|
||||
Starting with _version 4.3.6_, when using NIO, you can specify an `ssl-handshake-timeout` (seconds) on the connection factory.
|
||||
This timeout (default 30) is used during SSL handshake when waiting for data; if the timeout is exceeded, the process is aborted and the socket closed.
|
||||
|
||||
==== Advanced Techniques
|
||||
[[advanced-techniques]]
|
||||
=== Advanced Techniques
|
||||
|
||||
==== Strategy Interfaces
|
||||
|
||||
In many cases, the configuration described above is all that is needed to enable secure communication over TCP/IP.
|
||||
However, a number of strategy interfaces are provided to allow customization and modification of socket factories and sockets.
|
||||
@@ -904,18 +907,19 @@ However, a number of strategy interfaces are provided to allow customization and
|
||||
* `TcpSSLContextSupport`
|
||||
* `TcpSocketFactorySupport`
|
||||
* `TcpSocketSupport`
|
||||
* `TcpNioConnectionSupport`
|
||||
|
||||
[source,java]
|
||||
----
|
||||
public interface TcpSSLContextSupport {
|
||||
|
||||
SSLContext getSSLContext() throws Exception;
|
||||
SSLContext getSSLContext() throws Exception;
|
||||
|
||||
}
|
||||
----
|
||||
|
||||
Implementations of this interface are responsible for creating an SSLContext.
|
||||
The sole implementation provided by the framework is the `DefaultTcpSSLContextSupport` described above.
|
||||
The implementation provided by the framework is the `DefaultTcpSSLContextSupport` described above.
|
||||
If you require different behavior, implement this interface and provide the connection factory with a reference to a bean of your class' implementation.
|
||||
|
||||
[source,java]
|
||||
@@ -931,8 +935,8 @@ public interface TcpSocketFactorySupport {
|
||||
----
|
||||
|
||||
Implementations of this interface are responsible for obtaining references to `ServerSocketFactory` and `SocketFactory`.
|
||||
Two implementations are provided; the first is `DefaultTcpNetSocketFactorySupport` for non-SSL sockets (when no 'ssl-context-support' attribute is defined); this simply uses the JDK's default factories.
|
||||
The second implementation is `DefaultTcpNetSSLSocketFactorySupport`; this is used, by default, when an 'ssl-context-support' attribute is defined; it uses the `SSLContext` created by that bean to create the socket factories.
|
||||
Two implementations are provided; the first is `DefaultTcpNetSocketFactorySupport` for non-SSL sockets (when no `ssl-context-support` attribute is defined); this simply uses the JDK's default factories.
|
||||
The second implementation is `DefaultTcpNetSSLSocketFactorySupport`; this is used, by default, when an `ssl-context-support` attribute is defined; it uses the `SSLContext` created by that bean to create the socket factories.
|
||||
|
||||
NOTE: This interface only applies if `using-nio` is "false"; socket factories are not used by NIO.
|
||||
|
||||
@@ -944,7 +948,7 @@ public interface TcpSocketSupport {
|
||||
|
||||
void postProcessSocket(Socket socket);
|
||||
|
||||
|
||||
}
|
||||
----
|
||||
|
||||
Implementations of this interface can modify sockets after they are created, and after all configured attributes have been applied, but before the sockets are used.
|
||||
@@ -954,6 +958,68 @@ The sole implementation provided by the framework is the `DefaultTcpSocketSuppor
|
||||
|
||||
To supply your own implementation of `TcpSocketFactorySupport` or `TcpSocketSupport`, provide the connection factory with references to beans of your custom type using the `socket-factory-support` and `socket-support` attributes, respectively.
|
||||
|
||||
[source, java]
|
||||
----
|
||||
public interface TcpNioConnectionSupport {
|
||||
|
||||
TcpNioConnection createNewConnection(SocketChannel socketChannel,
|
||||
boolean server, boolean lookupHost,
|
||||
ApplicationEventPublisher applicationEventPublisher,
|
||||
String connectionFactoryName) throws Exception;
|
||||
|
||||
}
|
||||
----
|
||||
|
||||
This interface is invoked to create `TcpNioConnection` objects (or subclasses).
|
||||
Two implementations are provided `DefaultTcpNioSSLConnectionSupport` and `DefaultTcpNioConnectionSupport` which are used depending on whether SSL is in use or not.
|
||||
A common use case would be to subclass `DefaultTcpNioSSLConnectionSupport` and override `postProcessSSLEngine`; see the example below.
|
||||
|
||||
==== Example: Enabling SSL Client Authentication
|
||||
|
||||
To enable client certificate authentication when using SSL, the technique depends on whether NIO is in use or not.
|
||||
When NIO is not being used, provide a custom `TcpSocketSupport` implementation to post-process the server socket:
|
||||
|
||||
[source, java]
|
||||
----
|
||||
serverFactory.setTcpSocketSupport(new DefaultTcpSocketSupport() {
|
||||
|
||||
@Override
|
||||
public void postProcessServerSocket(ServerSocket serverSocket) {
|
||||
((SSLServerSocket) serverSocket).setNeedClientAuth(true);
|
||||
}
|
||||
|
||||
});
|
||||
----
|
||||
|
||||
(When using XML configuration, provide a reference to your bean using the `socket-support` attribute).
|
||||
|
||||
When using NIO, provide a custom `TcpNioSslConnectionSupport` implementation to post-process the `SSLEngine`.
|
||||
|
||||
[source, java]
|
||||
----
|
||||
@Bean
|
||||
public DefaultTcpNioSSLConnectionSupport tcpNioConnectionSupport() {
|
||||
return new DefaultTcpNioSSLConnectionSupport(serverSslContextSupport) {
|
||||
|
||||
@Override
|
||||
protected void postProcessSSLEngine(SSLEngine sslEngine) {
|
||||
sslEngine.setNeedClientAuth(true);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Bean
|
||||
public TcpNioServerConnectionFactory server() {
|
||||
...
|
||||
serverFactory.setTcpNioConnectionSupport(tcpNioConnectionSupport());
|
||||
...
|
||||
}
|
||||
----
|
||||
|
||||
(When using XML configuration, since _version 4.3.7_, provide a reference to your bean using the `nio-connection-support` attribute).
|
||||
|
||||
|
||||
[[ip-endpoint-reference]]
|
||||
=== IP Configuration Attributes
|
||||
|
||||
@@ -1116,6 +1182,11 @@ Defaults to true.
|
||||
| Y
|
||||
|
|
||||
| See <<ssl-tls>>
|
||||
| nio-connection-support
|
||||
| Y
|
||||
| Y
|
||||
|
|
||||
| See <<advanced-techniques>>
|
||||
| read-delay
|
||||
| Y
|
||||
| Y
|
||||
|
||||
Reference in New Issue
Block a user