This is an automated email from the ASF dual-hosted git repository. tabish pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/qpid-protonj2.git
The following commit(s) were added to refs/heads/main by this push: new febc0aea PROTON-2719 Ensure configured virtual host value is sent to the remote febc0aea is described below commit febc0aea5306a21bb65f388abb79c0950b5270d7 Author: Timothy Bish <tabish...@gmail.com> AuthorDate: Mon May 1 15:29:06 2023 -0400 PROTON-2719 Ensure configured virtual host value is sent to the remote The connection options virtual host should be used to override the value set for the connection host or to omit a value if empty string is provided. The host option should be applied both to the Open performative and to the SASLInit performative consistently. --- .../qpid/protonj2/client/ConnectionOptions.java | 7 ++ .../protonj2/client/impl/ClientConnection.java | 9 ++- .../protonj2/client/ConnectionOptionsTest.java | 93 +++++++++++++++++++++- .../qpid/protonj2/client/impl/ConnectionTest.java | 82 +++++++++++++++++++ 4 files changed, 187 insertions(+), 4 deletions(-) diff --git a/protonj2-client/src/main/java/org/apache/qpid/protonj2/client/ConnectionOptions.java b/protonj2-client/src/main/java/org/apache/qpid/protonj2/client/ConnectionOptions.java index 3f892028..c37e2990 100644 --- a/protonj2-client/src/main/java/org/apache/qpid/protonj2/client/ConnectionOptions.java +++ b/protonj2-client/src/main/java/org/apache/qpid/protonj2/client/ConnectionOptions.java @@ -130,6 +130,7 @@ public class ConnectionOptions implements Cloneable { other.reconnectedHandler(reconnectedHandler); other.disconnectedHandler(disconnectedHandler); other.defaultNextReceiverPolicy(nextReceiverPolicy); + other.virtualHost(virtualHost); if (offeredCapabilities != null) { other.offeredCapabilities(Arrays.copyOf(offeredCapabilities, offeredCapabilities.length)); @@ -501,6 +502,12 @@ public class ConnectionOptions implements Cloneable { /** * The virtual host value to provide to the remote when creating a new {@link Connection}. + * <p> + * A value of <code>null</code> signifies that the client should send the host value used + * to connect to the remote in the SASLInit and Open performatives, otherwise the value + * provided is sent unless empty string is provided in which case no AMQP host value is + * sent in either the SASLInit or the Open performative allowing the remote to select from + * a configured default. * * @param virtualHost * the virtual host to set diff --git a/protonj2-client/src/main/java/org/apache/qpid/protonj2/client/impl/ClientConnection.java b/protonj2-client/src/main/java/org/apache/qpid/protonj2/client/impl/ClientConnection.java index 876b01ca..97ae1d67 100644 --- a/protonj2-client/src/main/java/org/apache/qpid/protonj2/client/impl/ClientConnection.java +++ b/protonj2-client/src/main/java/org/apache/qpid/protonj2/client/impl/ClientConnection.java @@ -837,7 +837,7 @@ public final class ClientConnection implements Connection { @Override public String vhost() { - return options.virtualHost(); + return protonConnection.getHostname(); } @Override @@ -886,10 +886,15 @@ public final class ClientConnection implements Connection { protonConnection.setContainerId(connectionId); } + if (options.virtualHost() != null) { + protonConnection.setHostname(options.virtualHost().isEmpty() ? null : options.virtualHost()); + } else { + protonConnection.setHostname(location.getHost()); + } + protonConnection.setLinkedResource(this); protonConnection.setChannelMax(options.channelMax()); protonConnection.setMaxFrameSize(options.maxFrameSize()); - protonConnection.setHostname(location.getHost()); protonConnection.setIdleTimeout((int) options.idleTimeout()); protonConnection.setOfferedCapabilities(ClientConversionSupport.toSymbolArray(options.offeredCapabilities())); protonConnection.setDesiredCapabilities(ClientConversionSupport.toSymbolArray(options.desiredCapabilities())); diff --git a/protonj2-client/src/test/java/org/apache/qpid/protonj2/client/ConnectionOptionsTest.java b/protonj2-client/src/test/java/org/apache/qpid/protonj2/client/ConnectionOptionsTest.java index 34eef532..3edda0c5 100644 --- a/protonj2-client/src/test/java/org/apache/qpid/protonj2/client/ConnectionOptionsTest.java +++ b/protonj2-client/src/test/java/org/apache/qpid/protonj2/client/ConnectionOptionsTest.java @@ -16,33 +16,122 @@ */ package org.apache.qpid.protonj2.client; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotSame; import static org.junit.jupiter.api.Assertions.assertNull; +import java.util.HashMap; +import java.util.Map; + import org.junit.jupiter.api.Test; public class ConnectionOptionsTest { @Test - void testCreate() { + public void testCreate() { ConnectionOptions options = new ConnectionOptions(); assertNull(options.password()); assertNull(options.user()); + assertNull(options.virtualHost()); } @Test - void testCopy() { + public void testCreateDefaultsTimeouts() { ConnectionOptions options = new ConnectionOptions(); + assertEquals(ConnectionOptions.DEFAULT_CHANNEL_MAX, options.channelMax()); + assertEquals(ConnectionOptions.DEFAULT_MAX_FRAME_SIZE, options.maxFrameSize()); + assertEquals(ConnectionOptions.DEFAULT_OPEN_TIMEOUT, options.openTimeout()); + assertEquals(ConnectionOptions.DEFAULT_CLOSE_TIMEOUT, options.closeTimeout()); + assertEquals(ConnectionOptions.DEFAULT_SEND_TIMEOUT, options.sendTimeout()); + assertEquals(ConnectionOptions.DEFAULT_REQUEST_TIMEOUT, options.requestTimeout()); + assertEquals(ConnectionOptions.DEFAULT_IDLE_TIMEOUT, options.idleTimeout()); + assertEquals(ConnectionOptions.DEFAULT_DRAIN_TIMEOUT, options.drainTimeout()); + } + + @Test + public void testCopy() { + final ConnectionOptions options = new ConnectionOptions(); + final String[] offeredCapabilities = new String[] { "one", "two", "three" }; + final String[] desiredCapabilities = new String[] { "four", "five", "six" }; + final Map<String, Object> properties = new HashMap<String, Object>(); + properties.put("one", "1"); + properties.put("two", "2"); + options.user("test"); options.password("test-pass"); + options.virtualHost("test"); + options.openTimeout(10); + options.closeTimeout(20); + options.sendTimeout(30); + options.requestTimeout(40); + options.idleTimeout(50); + options.drainTimeout(60); + options.channelMax(1); + options.maxFrameSize(1024); + options.traceFrames(true); + options.defaultNextReceiverPolicy(NextReceiverPolicy.FIRST_AVAILABLE); + options.offeredCapabilities(offeredCapabilities); + options.desiredCapabilities(desiredCapabilities); + options.properties(properties); + options.transportOptions().allowNativeIO(false); + options.transportOptions().defaultTcpPort(8086); + options.sslOptions().allowNativeSSL(false); + options.sslOptions().defaultSslPort(8087); + options.saslOptions().saslEnabled(false); ConnectionOptions copy = options.clone(); assertNotSame(copy, options); + assertNotSame(copy.saslOptions(), options.saslOptions()); + assertNotSame(copy.transportOptions(), options.transportOptions()); + assertNotSame(copy.sslOptions(), options.sslOptions()); + assertNotSame(copy.offeredCapabilities(), options.offeredCapabilities()); + assertNotSame(copy.desiredCapabilities(), options.desiredCapabilities()); + assertNotSame(copy.properties(), options.properties()); + + assertArrayEquals(copy.offeredCapabilities(), offeredCapabilities); + assertArrayEquals(copy.desiredCapabilities(), desiredCapabilities); + + assertEquals(options.properties(), properties); assertEquals(options.user(), copy.user()); assertEquals(options.password(), copy.password()); + assertEquals(options.virtualHost(), copy.virtualHost()); + assertEquals(options.openTimeout(), copy.openTimeout()); + assertEquals(options.closeTimeout(), copy.closeTimeout()); + assertEquals(options.sendTimeout(), copy.sendTimeout()); + assertEquals(options.requestTimeout(), copy.requestTimeout()); + assertEquals(options.idleTimeout(), copy.idleTimeout()); + assertEquals(options.drainTimeout(), copy.drainTimeout()); + assertEquals(options.channelMax(), copy.channelMax()); + assertEquals(options.maxFrameSize(), copy.maxFrameSize()); + assertEquals(options.traceFrames(), copy.traceFrames()); + assertEquals(options.defaultNextReceiverPolicy(), copy.defaultNextReceiverPolicy()); + assertEquals(options.saslOptions().saslEnabled(), copy.saslOptions().saslEnabled()); + assertEquals(options.saslOptions().saslEnabled(), copy.saslOptions().saslEnabled()); + assertEquals(options.transportOptions().defaultTcpPort(), copy.transportOptions().defaultTcpPort()); + assertEquals(options.transportOptions().allowNativeIO(), copy.transportOptions().allowNativeIO()); + assertEquals(options.sslOptions().defaultSslPort(), copy.sslOptions().defaultSslPort()); + assertEquals(options.sslOptions().allowNativeSSL(), copy.sslOptions().allowNativeSSL()); + assertEquals(options.saslOptions().saslEnabled(), copy.saslOptions().saslEnabled()); + } + + @Test + public void testCopySaslOptions() { + ConnectionOptions options = new ConnectionOptions(); + + options.saslOptions().addAllowedMechanism("PLAIN"); + options.saslOptions().addAllowedMechanism("ANONYMOUS"); + options.saslOptions().saslEnabled(false); + + ConnectionOptions copy = options.clone(); + + assertNotSame(copy, options); + assertNotSame(copy.saslOptions(), options.saslOptions()); + + assertEquals(options.saslOptions().allowedMechanisms(), copy.saslOptions().allowedMechanisms()); + assertEquals(options.saslOptions().saslEnabled(), copy.saslOptions().saslEnabled()); } } diff --git a/protonj2-client/src/test/java/org/apache/qpid/protonj2/client/impl/ConnectionTest.java b/protonj2-client/src/test/java/org/apache/qpid/protonj2/client/impl/ConnectionTest.java index 38973302..f60bee32 100644 --- a/protonj2-client/src/test/java/org/apache/qpid/protonj2/client/impl/ConnectionTest.java +++ b/protonj2-client/src/test/java/org/apache/qpid/protonj2/client/impl/ConnectionTest.java @@ -56,6 +56,7 @@ import org.apache.qpid.protonj2.test.driver.ProtonTestServer; import org.apache.qpid.protonj2.test.driver.ProtonTestServerOptions; import org.apache.qpid.protonj2.test.driver.codec.messaging.TerminusDurability; import org.apache.qpid.protonj2.test.driver.codec.messaging.TerminusExpiryPolicy; +import org.apache.qpid.protonj2.test.driver.codec.security.SaslCode; import org.apache.qpid.protonj2.test.driver.matchers.messaging.SourceMatcher; import org.apache.qpid.protonj2.types.messaging.AmqpValue; import org.apache.qpid.protonj2.types.transport.AMQPHeader; @@ -1745,6 +1746,87 @@ public class ConnectionTest extends ImperativeClientTestCase { } } + @Test + public void testCreateConnectionWithNoVHostCarriesRemoteHost() throws Exception { + try (ProtonTestServer peer = new ProtonTestServer(testServerOptions())) { + peer.start(); + + final URI remoteURI = peer.getServerURI(); + + LOG.info("Connect test started, peer listening on: {}", remoteURI); + + peer.expectSASLHeader().respondWithSASLHeader(); + peer.remoteSaslMechanisms().withMechanisms("ANONYMOUS").queue(); + peer.expectSaslInit().withMechanism("ANONYMOUS").withHostname(remoteURI.getHost()); + peer.remoteSaslOutcome().withCode(SaslCode.OK).queue(); + peer.expectAMQPHeader().respondWithAMQPHeader(); + peer.expectOpen().withHostname(remoteURI.getHost()).respond(); + peer.expectClose().respond(); + + Client container = Client.create(); + Connection connection = container.connect(remoteURI.getHost(), remoteURI.getPort(), connectionOptions()); + + connection.openFuture().get(10, TimeUnit.SECONDS); + connection.closeAsync().get(10, TimeUnit.SECONDS); + + peer.waitForScriptToComplete(5, TimeUnit.SECONDS); + } + } + + @Test + public void testCreateConnectionWithSpecifiedVHost() throws Exception { + try (ProtonTestServer peer = new ProtonTestServer(testServerOptions())) { + peer.expectSASLHeader().respondWithSASLHeader(); + peer.remoteSaslMechanisms().withMechanisms("ANONYMOUS").queue(); + peer.expectSaslInit().withMechanism("ANONYMOUS").withHostname("test"); + peer.remoteSaslOutcome().withCode(SaslCode.OK).queue(); + peer.expectAMQPHeader().respondWithAMQPHeader(); + peer.expectOpen().withHostname("test").respond(); + peer.expectClose().respond(); + peer.start(); + + final URI remoteURI = peer.getServerURI(); + + LOG.info("Connect test started, peer listening on: {}", remoteURI); + + Client container = Client.create(); + ConnectionOptions options = connectionOptions().virtualHost("test"); + Connection connection = container.connect(remoteURI.getHost(), remoteURI.getPort(), options); + + connection.openFuture().get(10, TimeUnit.SECONDS); + connection.closeAsync().get(10, TimeUnit.SECONDS); + + peer.waitForScriptToComplete(5, TimeUnit.SECONDS); + } + } + + @Test + public void testCreateConnectionWithEmptyVHostSendsNullValueInOpen() throws Exception { + try (ProtonTestServer peer = new ProtonTestServer(testServerOptions())) { + peer.expectSASLHeader().respondWithSASLHeader(); + peer.remoteSaslMechanisms().withMechanisms("ANONYMOUS").queue(); + peer.expectSaslInit().withMechanism("ANONYMOUS").withHostname(nullValue()); + peer.remoteSaslOutcome().withCode(SaslCode.OK).queue(); + peer.expectAMQPHeader().respondWithAMQPHeader(); + peer.expectOpen().withHostname(nullValue()).respond(); + peer.expectClose().respond(); + peer.start(); + + final URI remoteURI = peer.getServerURI(); + + LOG.info("Connect test started, peer listening on: {}", remoteURI); + + Client container = Client.create(); + ConnectionOptions options = connectionOptions().virtualHost(""); + Connection connection = container.connect(remoteURI.getHost(), remoteURI.getPort(), options); + + connection.openFuture().get(10, TimeUnit.SECONDS); + connection.closeAsync().get(10, TimeUnit.SECONDS); + + peer.waitForScriptToComplete(5, TimeUnit.SECONDS); + } + } + @Disabled("Disabled due to requirement of hard coded port") @Test public void testLocalPortOption() throws Exception { --------------------------------------------------------------------- To unsubscribe, e-mail: commits-unsubscr...@qpid.apache.org For additional commands, e-mail: commits-h...@qpid.apache.org