Skip to content

Commit ad8881b

Browse files
author
Stephen Asbury
committed
Merge branch 'v2.0.2'
2 parents b1ee45c + 33ed516 commit ad8881b

File tree

7 files changed

+196
-37
lines changed

7 files changed

+196
-37
lines changed

.travis.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@ cache:
2424
- "$HOME/.gradle/wrapper/"
2525
after_success:
2626
- "./gradlew test jacocoTestReport coveralls"
27-
- "./gradlew uploadArchives"
28-
- "test ${TRAVIS_PULL_REQUEST} != 'true' && test ${TRAVIS_BRANCH} = 'master' && ./gradlew closeAndReleaseRepository"
27+
- test ${TRAVIS_BRANCH} != 'master' && "./gradlew uploadArchives" # Disable master for now, it fails due to ip address issues
28+
#Disable for now, upload archives fails because of IP address changes - "test ${TRAVIS_PULL_REQUEST} != 'true' && test ${TRAVIS_BRANCH} = 'master' && ./gradlew closeAndReleaseRepository"
2929
env:
3030
global:
3131
- gnatsd_version=v1.2.0

.travis/deploying.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@
55

66
There are currently two steps to the deployment, travis + sonatype, with a few random extra bits of knowledge.
77

8-
Travis will either deploy a snapshot or a release based on the version in build.gradle. If you deploy a release build, you will need to manually go to sonatype to release it. Those builds are found in the staging area. (We need to try to automate this in the future.)
8+
Travis doesn't support sonatype deploy correctly. There is an issue where the various artifacts get split across multiple repositories. That code has been deleted from the travis file and manual releases are required.
9+
10+
~~Travis will either deploy a snapshot or a release based on the version in build.gradle. If you deploy a release build, you will need to manually go to sonatype to release it. Those builds are found in the staging area. (We need to try to automate this in the future.)~~
911

1012
## Important note about release repositories
1113

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11

22
# Change Log
33

4+
## Version 2.0.0
5+
6+
* [FIXED] In a cluster situation the library wasn't using each server's auth info if it was in the URI.
7+
48
## Version 2.0.1
59

610
* [CHANGED] Request now returns a CompletableFuture to allow more application async options

src/main/java/io/nats/client/Options.java

Lines changed: 41 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -855,6 +855,9 @@ public Builder reconnectBufferSize(long size) {
855855
/**
856856
* Set the username and password for basic authentication.
857857
*
858+
* If the user and password are set in the server URL, they will override these values. However, in a clustering situation,
859+
* these values can be used as a fallback.
860+
*
858861
* @param userName a non-empty user name
859862
* @param password the password, in plain text
860863
* @return the Builder for chaining
@@ -868,6 +871,8 @@ public Builder userInfo(String userName, String password) {
868871
/**
869872
* Set the token for token-based authentication.
870873
*
874+
* If a token is provided in a server URI it overrides this value.
875+
*
871876
* @param token The token
872877
* @return the Builder for chaining
873878
*/
@@ -941,21 +946,6 @@ public Options build() throws IllegalStateException {
941946
server(DEFAULT_URL);
942947
} else if (servers.size() == 1) { // Allow some URI based configs
943948
URI serverURI = servers.get(0);
944-
945-
if (this.username==null && this.password==null && this.token == null) {
946-
String userInfo = serverURI.getUserInfo();
947-
948-
if (userInfo != null) {
949-
String[] info = userInfo.split(":");
950-
951-
if (info.length == 2) {
952-
this.username = info[0];
953-
this.password = info[1];
954-
} else {
955-
this.token = userInfo;
956-
}
957-
}
958-
}
959949

960950
if ("tls".equals(serverURI.getScheme()) && this.sslContext == null)
961951
{
@@ -1188,10 +1178,13 @@ public boolean isOldRequestStyle() {
11881178
/**
11891179
* Create the options string sent with a connect message.
11901180
*
1181+
* If includeAuth is true the auth information is included:
1182+
* If the server URIs have auth info it is used. Otherwise the userInfo is used.
1183+
*
11911184
* @param includeAuth tells the options to build a connection string that includes auth information
11921185
* @return the options String, basically JSON
11931186
*/
1194-
public String buildProtocolConnectOptionsString(boolean includeAuth) {
1187+
public String buildProtocolConnectOptionsString(String serverURI, boolean includeAuth) {
11951188
StringBuilder connectString = new StringBuilder();
11961189
connectString.append("{");
11971190

@@ -1210,15 +1203,44 @@ public String buildProtocolConnectOptionsString(boolean includeAuth) {
12101203
appendOption(connectString, Options.OPTION_ECHO, String.valueOf(!this.isNoEcho()), false, true);
12111204

12121205
if (includeAuth) {
1213-
if (this.username != null) {
1206+
String uriUser = null;
1207+
String uriPass = null;
1208+
String uriToken = null;
1209+
1210+
// Values from URI override options
1211+
try {
1212+
URI uri = new URI(serverURI);
1213+
String userInfo = uri.getUserInfo();
1214+
1215+
if (userInfo != null) {
1216+
String[] info = userInfo.split(":");
1217+
1218+
if (info.length == 2) {
1219+
uriUser = info[0];
1220+
uriPass = info[1];
1221+
} else {
1222+
uriToken = userInfo;
1223+
}
1224+
}
1225+
} catch(URISyntaxException e) {
1226+
uriUser = uriToken = uriPass = null;
1227+
}
1228+
1229+
if (uriUser != null) {
1230+
appendOption(connectString, Options.OPTION_USER, uriUser, true, true);
1231+
} else if (this.username != null) {
12141232
appendOption(connectString, Options.OPTION_USER, this.username, true, true);
12151233
}
12161234

1217-
if (this.password != null) {
1235+
if (uriPass != null) {
1236+
appendOption(connectString, Options.OPTION_PASSWORD, uriPass, true, true);
1237+
} else if (this.password != null) {
12181238
appendOption(connectString, Options.OPTION_PASSWORD, this.password, true, true);
12191239
}
12201240

1221-
if (this.token != null) {
1241+
if (uriToken != null) {
1242+
appendOption(connectString, Options.OPTION_AUTH_TOKEN, uriToken, true, true);
1243+
} else if (this.token != null) {
12221244
appendOption(connectString, Options.OPTION_AUTH_TOKEN, this.token, true, true);
12231245
}
12241246
}

src/main/java/io/nats/client/impl/NatsConnection.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -293,7 +293,7 @@ void tryToConnect(String serverURI) {
293293
this.reader.start(this.dataPortFuture);
294294
this.writer.start(this.dataPortFuture);
295295

296-
this.sendConnect();
296+
this.sendConnect(serverURI);
297297
Future<Boolean> pongFuture = sendPing();
298298
pongFuture.get(connectTimeout.toNanos(), TimeUnit.NANOSECONDS);
299299

@@ -884,12 +884,12 @@ public void flush(Duration timeout) throws TimeoutException, InterruptedExceptio
884884
}
885885
}
886886

887-
void sendConnect() {
887+
void sendConnect(String serverURI) {
888888
NatsServerInfo info = this.serverInfo.get();
889889
StringBuilder connectString = new StringBuilder();
890890
connectString.append(NatsConnection.OP_CONNECT);
891891
connectString.append(" ");
892-
String connectOptions = this.options.buildProtocolConnectOptionsString(info.isAuthRequired());
892+
String connectOptions = this.options.buildProtocolConnectOptionsString(serverURI, info.isAuthRequired());
893893
connectString.append(connectOptions);
894894
NatsMessage msg = new NatsMessage(connectString.toString());
895895
queueOutgoing(msg);

src/test/java/io/nats/client/AuthTests.java

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,17 @@
1313

1414
package io.nats.client;
1515

16+
import static org.junit.Assert.assertEquals;
1617
import static org.junit.Assert.assertTrue;
1718

1819
import java.io.IOException;
20+
import java.time.Duration;
21+
import java.util.concurrent.TimeUnit;
1922

2023
import org.junit.Test;
2124

25+
import io.nats.client.ConnectionListener.Events;
26+
2227
public class AuthTests {
2328
@Test
2429
public void testUserPass() throws Exception {
@@ -84,6 +89,128 @@ public void testUserPassInURL() throws Exception {
8489
}
8590
}
8691

92+
@Test
93+
public void testUserPassInURLClusteredWithDifferentUser() throws Exception {
94+
String[] customArgs1 = {"--user","stephen","--pass","password"};
95+
String[] customArgs2 = {"--user","alberto","--pass","casadecampo"};
96+
TestHandler handler = new TestHandler();
97+
try (NatsTestServer ts1 = new NatsTestServer(customArgs1, false);
98+
NatsTestServer ts2 = new NatsTestServer(customArgs2, false)) {
99+
// See config file for user/pass
100+
Options options = new Options.Builder().
101+
server("nats://stephen:password@localhost:"+ts1.getPort()).
102+
server("nats://alberto:casadecampo@localhost:"+ts2.getPort()).
103+
maxReconnects(4).
104+
noRandomize().
105+
connectionListener(handler).
106+
pingInterval(Duration.ofMillis(100)).
107+
build();
108+
Connection nc = Nats.connect(options);
109+
assertTrue("Connected Status", Connection.Status.CONNECTED == nc.getStatus());
110+
assertEquals(nc.getConnectedUrl(), "nats://stephen:password@localhost:"+ts1.getPort());
111+
112+
handler.prepForStatusChange(Events.RESUBSCRIBED);
113+
ts1.close();
114+
handler.waitForStatusChange(2, TimeUnit.SECONDS);
115+
116+
assertTrue("Connected Status", Connection.Status.CONNECTED == nc.getStatus());
117+
assertEquals(nc.getConnectedUrl(), "nats://alberto:casadecampo@localhost:"+ts2.getPort());
118+
nc.close();
119+
}
120+
}
121+
122+
@Test
123+
public void testUserPassInURLWithFallback() throws Exception {
124+
String[] customArgs1 = {"--user","stephen","--pass","password"};
125+
String[] customArgs2 = {"--user","alberto","--pass","casadecampo"};
126+
TestHandler handler = new TestHandler();
127+
try (NatsTestServer ts1 = new NatsTestServer(customArgs1, false);
128+
NatsTestServer ts2 = new NatsTestServer(customArgs2, false)) {
129+
// See config file for user/pass
130+
Options options = new Options.Builder().
131+
server("nats://stephen:password@localhost:"+ts1.getPort()).
132+
server("nats://localhost:"+ts2.getPort()).
133+
userInfo("alberto", "casadecampo").
134+
maxReconnects(4).
135+
noRandomize().
136+
connectionListener(handler).
137+
pingInterval(Duration.ofMillis(100)).
138+
build();
139+
Connection nc = Nats.connect(options);
140+
assertTrue("Connected Status", Connection.Status.CONNECTED == nc.getStatus());
141+
assertEquals(nc.getConnectedUrl(), "nats://stephen:password@localhost:"+ts1.getPort());
142+
143+
handler.prepForStatusChange(Events.RESUBSCRIBED);
144+
ts1.close();
145+
handler.waitForStatusChange(2, TimeUnit.SECONDS);
146+
147+
assertTrue("Connected Status", Connection.Status.CONNECTED == nc.getStatus());
148+
assertEquals(nc.getConnectedUrl(), "nats://localhost:"+ts2.getPort());
149+
nc.close();
150+
}
151+
}
152+
153+
@Test
154+
public void testTokenInURLClusteredWithDifferentUser() throws Exception {
155+
String[] customArgs1 = {"--auth","token_one"};
156+
String[] customArgs2 = {"--auth","token_two"};
157+
TestHandler handler = new TestHandler();
158+
try (NatsTestServer ts1 = new NatsTestServer(customArgs1, false);
159+
NatsTestServer ts2 = new NatsTestServer(customArgs2, false)) {
160+
// See config file for user/pass
161+
Options options = new Options.Builder().
162+
server("nats://token_one@localhost:"+ts1.getPort()).
163+
server("nats://token_two@localhost:"+ts2.getPort()).
164+
maxReconnects(4).
165+
noRandomize().
166+
connectionListener(handler).
167+
pingInterval(Duration.ofMillis(100)).
168+
build();
169+
Connection nc = Nats.connect(options);
170+
assertTrue("Connected Status", Connection.Status.CONNECTED == nc.getStatus());
171+
assertEquals(nc.getConnectedUrl(), "nats://token_one@localhost:"+ts1.getPort());
172+
173+
handler.prepForStatusChange(Events.RESUBSCRIBED);
174+
ts1.close();
175+
handler.waitForStatusChange(2, TimeUnit.SECONDS);
176+
177+
assertTrue("Connected Status", Connection.Status.CONNECTED == nc.getStatus());
178+
assertEquals(nc.getConnectedUrl(), "nats://token_two@localhost:"+ts2.getPort());
179+
nc.close();
180+
}
181+
}
182+
183+
@Test
184+
public void testTokenInURLWithFallback() throws Exception {
185+
String[] customArgs1 = {"--auth","token_one"};
186+
String[] customArgs2 = {"--auth","token_two"};
187+
TestHandler handler = new TestHandler();
188+
try (NatsTestServer ts1 = new NatsTestServer(customArgs1, false);
189+
NatsTestServer ts2 = new NatsTestServer(customArgs2, false)) {
190+
// See config file for user/pass
191+
Options options = new Options.Builder().
192+
server("nats://token_one@localhost:"+ts1.getPort()).
193+
server("nats://localhost:"+ts2.getPort()).
194+
token("token_two").
195+
maxReconnects(4).
196+
noRandomize().
197+
connectionListener(handler).
198+
pingInterval(Duration.ofMillis(100)).
199+
build();
200+
Connection nc = Nats.connect(options);
201+
assertTrue("Connected Status", Connection.Status.CONNECTED == nc.getStatus());
202+
assertEquals(nc.getConnectedUrl(), "nats://token_one@localhost:"+ts1.getPort());
203+
204+
handler.prepForStatusChange(Events.RESUBSCRIBED);
205+
ts1.close();
206+
handler.waitForStatusChange(2, TimeUnit.SECONDS);
207+
208+
assertTrue("Connected Status", Connection.Status.CONNECTED == nc.getStatus());
209+
assertEquals(nc.getConnectedUrl(), "nats://localhost:"+ts2.getPort());
210+
nc.close();
211+
}
212+
}
213+
87214
@Test
88215
public void testToken() throws Exception {
89216
String[] customArgs = {"--auth","derek"};

src/test/java/io/nats/client/OptionsTests.java

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -286,7 +286,7 @@ public void testDefaultConnectOptions() {
286286
Options o = new Options.Builder().build();
287287
String expected = "{\"lang\":\"java\",\"version\":\"" + Nats.CLIENT_VERSION + "\""
288288
+ ",\"protocol\":1,\"verbose\":false,\"pedantic\":false,\"tls_required\":false,\"echo\":true}";
289-
assertEquals("default connect options", expected, o.buildProtocolConnectOptionsString(false));
289+
assertEquals("default connect options", expected, o.buildProtocolConnectOptionsString("nats://localhost:4222", false));
290290
}
291291

292292
@Test
@@ -295,7 +295,7 @@ public void testConnectOptionsWithNameAndContext() throws Exception {
295295
Options o = new Options.Builder().sslContext(ctx).connectionName("c1").build();
296296
String expected = "{\"lang\":\"java\",\"version\":\"" + Nats.CLIENT_VERSION + "\",\"name\":\"c1\""
297297
+ ",\"protocol\":1,\"verbose\":false,\"pedantic\":false,\"tls_required\":true,\"echo\":true}";
298-
assertEquals("default connect options", expected, o.buildProtocolConnectOptionsString(false));
298+
assertEquals("default connect options", expected, o.buildProtocolConnectOptionsString("nats://localhost:4222", false));
299299
}
300300

301301
@Test
@@ -306,8 +306,8 @@ public void testAuthConnectOptions() {
306306
String expectedWithAuth = "{\"lang\":\"java\",\"version\":\"" + Nats.CLIENT_VERSION + "\""
307307
+ ",\"protocol\":1,\"verbose\":false,\"pedantic\":false,\"tls_required\":false,\"echo\":true"
308308
+ ",\"user\":\"hello\",\"pass\":\"world\"}";
309-
assertEquals("no auth connect options", expectedNoAuth, o.buildProtocolConnectOptionsString(false));
310-
assertEquals("auth connect options", expectedWithAuth, o.buildProtocolConnectOptionsString(true));
309+
assertEquals("no auth connect options", expectedNoAuth, o.buildProtocolConnectOptionsString("nats://localhost:4222", false));
310+
assertEquals("auth connect options", expectedWithAuth, o.buildProtocolConnectOptionsString("nats://localhost:4222", true));
311311
}
312312

313313
@Test
@@ -333,20 +333,24 @@ public void testPropertyDataPortType() {
333333

334334
@Test
335335
public void testUserPassInURL() {
336-
Options o = new Options.Builder().server("nats://derek:password@localhost:2222").build();
336+
String serverURI = "nats://derek:password@localhost:2222";
337+
Options o = new Options.Builder().server(serverURI).build();
337338

338-
assertNull(o.getToken());
339-
assertEquals("user from url", "derek", o.getUsername());
340-
assertEquals("password from url", "password", o.getPassword());
339+
String connectString = o.buildProtocolConnectOptionsString(serverURI, true);
340+
assertTrue(connectString.contains("\"user\":\"derek\""));
341+
assertTrue(connectString.contains("\"pass\":\"password\""));
342+
assertFalse(connectString.contains("\"token\":"));
341343
}
342344

343345
@Test
344346
public void testTokenInURL() {
345-
Options o = new Options.Builder().server("nats://alberto@localhost:2222").build();
347+
String serverURI = "nats://alberto@localhost:2222";
348+
Options o = new Options.Builder().server(serverURI).build();
346349

347-
assertNull(o.getUsername());
348-
assertNull(o.getPassword());
349-
assertEquals("token from url", "alberto", o.getToken());
350+
String connectString = o.buildProtocolConnectOptionsString(serverURI, true);
351+
assertTrue(connectString.contains("\"auth_token\":\"alberto\""));
352+
assertFalse(connectString.contains("\"user\":"));
353+
assertFalse(connectString.contains("\"pass\":"));
350354
}
351355

352356
@Test(expected=IllegalArgumentException.class)

0 commit comments

Comments
 (0)