diff --git a/client/src/main/java/org/glassfish/tyrus/client/ClientProperties.java b/client/src/main/java/org/glassfish/tyrus/client/ClientProperties.java
index 39062b7f..cf84f8ad 100644
--- a/client/src/main/java/org/glassfish/tyrus/client/ClientProperties.java
+++ b/client/src/main/java/org/glassfish/tyrus/client/ClientProperties.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014, 2017 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2020 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
@@ -17,6 +17,7 @@
package org.glassfish.tyrus.client;
+import java.net.InetAddress;
import java.net.URI;
import org.glassfish.tyrus.client.auth.AuthConfig;
@@ -284,4 +285,15 @@ public final class ClientProperties {
* of masking keys.
*/
public static final String MASKING_KEY_GENERATOR = "org.glassfish.tyrus.client.maskingKeyGenerator";
-}
+
+ /**
+ * Property name for defining local binding address for all socket created by the client. The expected value is an instance
+ * of {@link java.net.InetAddress}.
+ *
+ * Sample below demonstrates how to use this property:
+ *
+ * client.getProperties().put(ClientProperties.SOCKET_BINDING, InetAddress.getByName("127.0.0.1"));
+ *
+ */
+ public static final String SOCKET_BINDING = "org.glassfish.tyrus.client.socketBinding";
+}
\ No newline at end of file
diff --git a/containers/grizzly-client/src/main/java/org/glassfish/tyrus/container/grizzly/client/GrizzlyClientSocket.java b/containers/grizzly-client/src/main/java/org/glassfish/tyrus/container/grizzly/client/GrizzlyClientSocket.java
index 63394177..d3388d59 100644
--- a/containers/grizzly-client/src/main/java/org/glassfish/tyrus/container/grizzly/client/GrizzlyClientSocket.java
+++ b/containers/grizzly-client/src/main/java/org/glassfish/tyrus/container/grizzly/client/GrizzlyClientSocket.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011, 2017 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2020 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
@@ -17,6 +17,7 @@
package org.glassfish.tyrus.container.grizzly.client;
import java.io.IOException;
+import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.net.ProxySelector;
@@ -359,7 +360,13 @@ public void handleTimeout() {
sharedTransportTimeout, proxyHeaders, grizzlyConnector, sslHandshakeFuture,
upgradeRequest));
- connectionGrizzlyFuture = connectorHandler.connect(connectAddress);
+ InetAddress bindingAddress = Utils.getProperty(properties, ClientProperties.SOCKET_BINDING, InetAddress.class);
+
+ if (bindingAddress == null) {
+ connectionGrizzlyFuture = connectorHandler.connect(connectAddress);
+ } else {
+ connectionGrizzlyFuture = connectorHandler.connect(socketAddress, new InetSocketAddress(bindingAddress, 0));
+ }
try {
final Connection connection = connectionGrizzlyFuture.get(timeoutMs, TimeUnit.MILLISECONDS);
diff --git a/containers/jdk-client/src/main/java/org/glassfish/tyrus/container/jdk/client/JdkClientContainer.java b/containers/jdk-client/src/main/java/org/glassfish/tyrus/container/jdk/client/JdkClientContainer.java
index f4b6959b..a784ad95 100644
--- a/containers/jdk-client/src/main/java/org/glassfish/tyrus/container/jdk/client/JdkClientContainer.java
+++ b/containers/jdk-client/src/main/java/org/glassfish/tyrus/container/jdk/client/JdkClientContainer.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014, 2017 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2020 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
@@ -17,6 +17,7 @@
package org.glassfish.tyrus.container.jdk.client;
import java.io.IOException;
+import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.net.ProxySelector;
@@ -99,6 +100,9 @@ public void openClientSocket(final ClientEndpointConfig cec, final Map jdkConnector = new Callable() {
@@ -118,13 +122,19 @@ public Void call() throws DeploymentException {
if (secure) {
TransportFilter transportFilter =
- createTransportFilter(SSL_INPUT_BUFFER_SIZE, finalThreadPoolConfig, containerIdleTimeout);
+ createTransportFilter(SSL_INPUT_BUFFER_SIZE,
+ finalThreadPoolConfig,
+ containerIdleTimeout,
+ bindingAddress);
SslFilter sslFilter = createSslFilter(cec, properties, transportFilter, uri);
writeQueue = createTaskQueueFilter(sslFilter);
} else {
TransportFilter transportFilter =
- createTransportFilter(INPUT_BUFFER_SIZE, finalThreadPoolConfig, containerIdleTimeout);
+ createTransportFilter(INPUT_BUFFER_SIZE,
+ finalThreadPoolConfig,
+ containerIdleTimeout,
+ bindingAddress);
writeQueue = createTaskQueueFilter(transportFilter);
}
@@ -276,9 +286,11 @@ private SslFilter createSslFilter(ClientEndpointConfig cec, Map
return sslFilter;
}
- private TransportFilter createTransportFilter(int sslInputBufferSize, ThreadPoolConfig threadPoolConfig,
- Integer containerIdleTimeout) {
- return new TransportFilter(sslInputBufferSize, threadPoolConfig, containerIdleTimeout);
+ private TransportFilter createTransportFilter(int sslInputBufferSize,
+ ThreadPoolConfig threadPoolConfig,
+ Integer containerIdleTimeout,
+ InetAddress bindingAddress) {
+ return new TransportFilter(sslInputBufferSize, threadPoolConfig, containerIdleTimeout, bindingAddress);
}
private TaskQueueFilter createTaskQueueFilter(Filter downstreamFilter) {
diff --git a/containers/jdk-client/src/main/java/org/glassfish/tyrus/container/jdk/client/TransportFilter.java b/containers/jdk-client/src/main/java/org/glassfish/tyrus/container/jdk/client/TransportFilter.java
index 940d992f..6b561383 100644
--- a/containers/jdk-client/src/main/java/org/glassfish/tyrus/container/jdk/client/TransportFilter.java
+++ b/containers/jdk-client/src/main/java/org/glassfish/tyrus/container/jdk/client/TransportFilter.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014, 2017 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2020 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
@@ -17,6 +17,8 @@
package org.glassfish.tyrus.container.jdk.client;
import java.io.IOException;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousChannelGroup;
@@ -85,6 +87,7 @@ public Thread newThread(Runnable r) {
private final int inputBufferSize;
private final ThreadPoolConfig threadPoolConfig;
private final Integer containerIdleTimeout;
+ private final InetAddress bindingAddress;
private volatile AsynchronousSocketChannel socketChannel;
@@ -99,12 +102,18 @@ public Thread newThread(Runnable r) {
* @param threadPoolConfig thread pool configuration used for creating thread pool.
* @param containerIdleTimeout idle time after which the shared thread pool will be destroyed. If {@code null}
* default value will be used. The default value is 30 seconds.
+ * @param bindingAddress local address to bind sockets ({@link #socketChannel}) when they are created.
+ * Binding is done only if not {@code null}.
*/
- TransportFilter(int inputBufferSize, ThreadPoolConfig threadPoolConfig, Integer containerIdleTimeout) {
+ TransportFilter(int inputBufferSize,
+ ThreadPoolConfig threadPoolConfig,
+ Integer containerIdleTimeout,
+ InetAddress bindingAddress) {
super(null);
this.inputBufferSize = inputBufferSize;
this.threadPoolConfig = threadPoolConfig;
this.containerIdleTimeout = containerIdleTimeout;
+ this.bindingAddress = bindingAddress;
}
@Override
@@ -161,6 +170,9 @@ public void handleConnect(SocketAddress serverAddress, Filter upstreamFilter) {
updateThreadPoolConfig();
initializeChannelGroup();
socketChannel = AsynchronousSocketChannel.open(channelGroup);
+ if (bindingAddress != null) {
+ socketChannel.bind(new InetSocketAddress(bindingAddress, 0));
+ }
openedConnections.incrementAndGet();
}
} catch (IOException e) {
diff --git a/containers/jdk-client/src/test/java/org/glassfish/tyrus/container/jdk/client/SslFilterTest.java b/containers/jdk-client/src/test/java/org/glassfish/tyrus/container/jdk/client/SslFilterTest.java
index 0f043fc3..b9665ce1 100644
--- a/containers/jdk-client/src/test/java/org/glassfish/tyrus/container/jdk/client/SslFilterTest.java
+++ b/containers/jdk-client/src/test/java/org/glassfish/tyrus/container/jdk/client/SslFilterTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015, 2017 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2020 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
@@ -381,7 +381,7 @@ private Filter openClientSocket(String host, final ByteBuffer readBuffer, final
SslEngineConfigurator sslEngineConfigurator = new SslEngineConfigurator(sslConfig.createSSLContext());
sslEngineConfigurator.setHostnameVerifier(customHostnameVerifier);
- final TransportFilter transportFilter = new TransportFilter(17_000, ThreadPoolConfig.defaultConfig(), null);
+ final TransportFilter transportFilter = new TransportFilter(17_000, ThreadPoolConfig.defaultConfig(), null, null);
final SslFilter sslFilter = new SslFilter(transportFilter, sslEngineConfigurator, host);
// exceptions errors that occur before SSL handshake has finished are thrown from this method