Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 12 additions & 6 deletions core/src/main/java/io/ably/lib/rest/AblyBase.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package io.ably.lib.rest;

import io.ably.annotation.Experimental;
import io.ably.lib.debug.DebugOptions;
import io.ably.lib.http.AsyncHttpScheduler;
import io.ably.lib.http.Http;
import io.ably.lib.http.HttpCore;
Expand Down Expand Up @@ -83,17 +84,22 @@ public AblyBase(ClientOptions options, PlatformAgentProvider platformAgentProvid
Log.e(getClass().getName(), msg);
throw AblyException.fromErrorInfo(new ErrorInfo(msg, 400, 40000));
}
this.options = options;

if (options instanceof DebugOptions) {
this.options = options;
} else {
this.options = options.copy();
}

/* process options */
Log.setLevel(options.logLevel);
Log.setHandler(options.logHandler);
Log.setLevel(this.options.logLevel);
Log.setHandler(this.options.logHandler);
Log.i(getClass().getName(), "started");

this.platformAgentProvider = platformAgentProvider;
auth = new Auth(this, options);
httpCore = new HttpCore(options, auth, this.platformAgentProvider);
http = new Http(new AsyncHttpScheduler(httpCore, options), new SyncHttpScheduler(httpCore));
auth = new Auth(this, this.options);
httpCore = new HttpCore(this.options, auth, this.platformAgentProvider);
http = new Http(new AsyncHttpScheduler(httpCore, this.options), new SyncHttpScheduler(httpCore));

channels = (Channels<ChannelType>) new InternalChannels();

Expand Down
6 changes: 5 additions & 1 deletion core/src/main/java/io/ably/lib/rest/Auth.java
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,10 @@ public String asJson() {
*/
@Override
public boolean equals(Object obj) {
if (!(obj instanceof TokenDetails)) {
return false;
}

TokenDetails details = (TokenDetails)obj;
return equalNullableStrings(this.token, details.token) &
equalNullableStrings(this.capability, details.capability) &
Expand Down Expand Up @@ -360,7 +364,7 @@ private TokenParams storedValues() {
*
* @return copied object
*/
private TokenParams copy() {
public TokenParams copy() {
TokenParams result = new TokenParams();
result.ttl = this.ttl;
result.capability = this.capability;
Expand Down
94 changes: 94 additions & 0 deletions core/src/main/java/io/ably/lib/types/ClientOptions.java
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
package io.ably.lib.types;

import io.ably.lib.push.Storage;
import io.ably.lib.rest.Auth;
import io.ably.lib.rest.Auth.AuthOptions;
import io.ably.lib.rest.Auth.TokenParams;
import io.ably.lib.transport.Defaults;
import io.ably.lib.util.Log;
import io.ably.lib.util.Log.LogHandler;

import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

/**
Expand Down Expand Up @@ -220,4 +223,95 @@ public ClientOptions(String key) throws AblyException {
* Agent versions are optional, if you don't want to specify it pass `null` as the map entry value.
*/
public Map<String, String> agents;

/**
* Creates new copy of this object
* @return copy of ClientOptions
*/
public ClientOptions copy() {
final ClientOptions copyOptions = new ClientOptions();
copyOptions.clientId = this.clientId;
copyOptions.logLevel = this.logLevel;
copyOptions.logHandler = this.logHandler;
copyOptions.tls = this.tls;
copyOptions.restHost = this.restHost;
copyOptions.realtimeHost = this.realtimeHost;
copyOptions.port = this.port;
copyOptions.tlsPort = this.tlsPort;
copyOptions.autoConnect = this.autoConnect;
copyOptions.useBinaryProtocol = this.useBinaryProtocol;
copyOptions.queueMessages = this.queueMessages;
copyOptions.echoMessages = this.echoMessages;
copyOptions.recover = this.recover;
copyOptions.idempotentRestPublishing = this.idempotentRestPublishing;
copyOptions.httpOpenTimeout = this.httpOpenTimeout;
copyOptions.httpRequestTimeout = this.httpRequestTimeout;
copyOptions.httpMaxRetryCount = this.httpMaxRetryCount;
copyOptions.realtimeRequestTimeout = this.realtimeRequestTimeout;
copyOptions.fallbackHostsUseDefault = this.fallbackHostsUseDefault;
copyOptions.fallbackRetryTimeout = this.fallbackRetryTimeout;
copyOptions.defaultTokenParams = this.defaultTokenParams.copy();
copyOptions.channelRetryTimeout = this.channelRetryTimeout;
copyOptions.asyncHttpThreadpoolSize = this.asyncHttpThreadpoolSize;
copyOptions.pushFullWait = this.pushFullWait;
copyOptions.localStorage = this.localStorage;
copyOptions.addRequestIds = this.addRequestIds;
copyOptions.environment = this.environment;

//params from AuthOptions
copyOptions.authCallback = this.authCallback;
copyOptions.authUrl = this.authUrl;
copyOptions.authMethod = this.authMethod;
copyOptions.key = this.key;
copyOptions.token = this.token;
copyOptions.queryTime = this.queryTime;
copyOptions.useTokenAuth = this.useTokenAuth;

if (this.headers != null) {
copyOptions.headers = new HashMap<>(this.headers);
}

if (this.agents != null) {
copyOptions.agents = new HashMap<>(this.agents);
}

if (this.authParams != null) {
copyOptions.authParams = Arrays.copyOf(this.authParams, this.authParams.length);
}

if (this.authHeaders != null) {
copyOptions.authHeaders = Arrays.copyOf(this.authHeaders, this.authHeaders.length);
}

if (this.transportParams != null) {
copyOptions.transportParams = Arrays.copyOf(this.transportParams, this.transportParams.length);
}

if (this.fallbackHosts != null) {
copyOptions.fallbackHosts = Arrays.copyOf(this.fallbackHosts, this.fallbackHosts.length);
}

if (this.proxy != null) {
ProxyOptions po = new ProxyOptions();
po.host = this.proxy.host;
po.port = this.proxy.port;
po.username = this.proxy.username;
po.password = this.proxy.password;
po.nonProxyHosts = this.proxy.nonProxyHosts;
po.prefAuthType = this.proxy.prefAuthType;
copyOptions.proxy = po;
}

if (this.tokenDetails != null) {
Auth.TokenDetails tokenDetails = new Auth.TokenDetails();
tokenDetails.token = this.tokenDetails.token;
tokenDetails.expires = this.tokenDetails.expires;
tokenDetails.issued = this.tokenDetails.issued;
tokenDetails.capability = this.tokenDetails.capability;
tokenDetails.clientId = this.tokenDetails.clientId;
copyOptions.tokenDetails = tokenDetails;
}

return copyOptions;
}
}
119 changes: 119 additions & 0 deletions core/src/test/java/io/ably/lib/test/realtime/RealtimeAuthTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,17 +24,25 @@
import io.ably.lib.types.Message;
import io.ably.lib.types.Param;
import io.ably.lib.types.ProtocolMessage;
import io.ably.lib.types.ProxyOptions;
import io.ably.lib.util.Log;

import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.Timeout;

import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;

import java.util.HashMap;
import java.util.Map;

public abstract class RealtimeAuthTest extends ParameterizedTest {

@Rule
Expand Down Expand Up @@ -956,4 +964,115 @@ public Object getTokenRequest(Auth.TokenParams params) throws AblyException {
}
}

/**
* Verify that instance of ClientOptions cannot change AblyRealtime options once it is provided to constructor
*/
@Test
public void auth_client_options_immutable() {
try {
/* create token with clientId */
ClientOptions optsForToken = createOptions(testVars.keys[0].keyStr);
optsForToken.clientId = "token_clientId";
AblyBase<PushBase, Platform, RestChannelBase> ablyForToken = createAblyRest(optsForToken);
TokenDetails tokenDetails = ablyForToken.auth.requestToken(null, null);

/* create ably realtime */
ClientOptions opts = new ClientOptions();
opts.clientId = null;
opts.token = tokenDetails.token;
opts.autoConnect = false;
opts.headers = new HashMap<>();
opts.headers.put("old_key", "old_value");
opts.tokenDetails = new TokenDetails("my_old_details_token");
opts.logHandler = new Log.DefaultHandler();
opts.authCallback = new Auth.TokenCallback() {
@Override
public Object getTokenRequest(Auth.TokenParams params) {
return null;
}
};
opts.authHeaders = new Param[1];
opts.authHeaders[0] = new Param("old_key", "old_key");
opts.proxy = new ProxyOptions();
opts.proxy.host = "https://ably.com";
opts.proxy.port = 8080;
AblyRealtimeBase<PushBase, Platform, RealtimeChannelBase> ablyRealtime = createAblyRealtime(opts);

assertEquals("clientId given for options must be equal to the one in in AblyRealtime before change", opts.clientId, ablyRealtime.options.clientId);
String origClientId = ablyRealtime.options.clientId;
opts.clientId = "my_new_clientId";
assertNotEquals("clientId given for options should not be equal to the one in in AblyRealtime after change", opts.clientId, ablyRealtime.options.clientId);
assertEquals("clientId in AblyRealtime should be equal to original one after ClientOptions change", origClientId, ablyRealtime.options.clientId);

assertEquals("logLevel given for options must be equal to the one in in AblyRealtime before change", opts.logLevel, ablyRealtime.options.logLevel);
int origLogLevel = ablyRealtime.options.logLevel;
opts.logLevel = 33;
assertNotEquals("logLevel given for options should not be equal to the one in in AblyRealtime after change", opts.logLevel, ablyRealtime.options.logLevel);
assertEquals("logLevel in AblyRealtime should be equal to original one after ClientOptions change", origLogLevel, ablyRealtime.options.logLevel);

assertEquals("autoConnect given for options must be equal to the one in in AblyRealtime before change", opts.autoConnect, ablyRealtime.options.autoConnect);
boolean origAutoConnect = ablyRealtime.options.autoConnect;
opts.autoConnect = true;
assertNotEquals("autoConnect given for options should not be equal to the one in in AblyRealtime after change", opts.autoConnect, ablyRealtime.options.autoConnect);
assertEquals("autoConnect in AblyRealtime should be equal to original one after ClientOptions change", origAutoConnect, ablyRealtime.options.autoConnect);

assertEquals("logHandler given for options must be equal to the one in in AblyRealtime before change", opts.logHandler, ablyRealtime.options.logHandler);
Log.LogHandler origHandler = ablyRealtime.options.logHandler;
opts.logHandler = new Log.DefaultHandler();
assertNotEquals("logHandler given for options should not be equal to the one in in AblyRealtime after change", opts.logHandler, ablyRealtime.options.logHandler);
assertEquals("logHandler in AblyRealtime should be equal to original one after ClientOptions change", origHandler, ablyRealtime.options.logHandler);

assertEquals("authCallback given for options must be equal to the one in in AblyRealtime before change", opts.authCallback, ablyRealtime.options.authCallback);
Auth.TokenCallback origTokenCallback = ablyRealtime.options.authCallback;
opts.authCallback = new Auth.TokenCallback() {
@Override
public Object getTokenRequest(Auth.TokenParams params) {
return null;
}
};
assertNotEquals("authCallback given for options should not be equal to the one in in AblyRealtime after change", opts.authCallback, ablyRealtime.options.authCallback);
assertEquals("authCallback in AblyRealtime should be equal to original one after ClientOptions change", origTokenCallback, ablyRealtime.options.authCallback);

assertEquals("token given for options must be equal to the one in in AblyRealtime before change", opts.token, ablyRealtime.options.token);
String origToken = ablyRealtime.options.token;
opts.token = "my_new_token";
assertNotEquals("token given for options should not be equal to the one in in AblyRealtime after change", opts.token, ablyRealtime.options.token);
assertEquals("token in AblyRealtime should be equal to original one after ClientOptions change", origToken, ablyRealtime.options.token);

assertEquals("tokenDetails given for options must be equal to the one in in AblyRealtime before change", opts.tokenDetails, ablyRealtime.options.tokenDetails);
TokenDetails origTokenDetails = ablyRealtime.options.tokenDetails;
opts.tokenDetails = new TokenDetails("my_new_details_token");
assertNotEquals("tokenDetails given for options should not be equal to the one in in AblyRealtime after change", opts.tokenDetails, ablyRealtime.options.tokenDetails);
assertEquals("tokenDetails in AblyRealtime should be equal to original one after ClientOptions change", origTokenDetails, ablyRealtime.options.tokenDetails);

assertEquals("headers given for options must be equal to the one in in AblyRealtime before change", opts.headers, ablyRealtime.options.headers);
Map<String, String> origHeaders = ablyRealtime.options.headers;
String origHeaderElementValue = origHeaders.entrySet().iterator().next().getValue();
opts.headers.clear();
opts.headers.put("new_key", "new_value");
assertNotEquals("headers given for options should not be equal to the one in in AblyRealtime after change", opts.headers, ablyRealtime.options.headers);
assertEquals("headers in AblyRealtime should be equal to original one after ClientOptions change", origHeaders, ablyRealtime.options.headers);
assertEquals("first header in AblyRealtime should be equal to original one after ClientOptions change", origHeaderElementValue, ablyRealtime.options.headers.entrySet().iterator().next().getValue());

assertArrayEquals("authHeaders given for options must be equal to the one in in AblyRealtime before change", opts.authHeaders, ablyRealtime.options.authHeaders);
Param[] origParams = ablyRealtime.options.authHeaders;
String origParamValue = ablyRealtime.options.authHeaders[0].value;
opts.authHeaders[0] = new Param("new_key", "new_key");
assertNotEquals("authHeaders given for options should not be equal to the one in in AblyRealtime after change", opts.authHeaders, ablyRealtime.options.authHeaders);
assertArrayEquals("authHeaders in AblyRealtime should be equal to original one after ClientOptions change", origParams, ablyRealtime.options.authHeaders);
assertEquals("first authHeader in AblyRealtime should be equal to original one after ClientOptions change", origParamValue, ablyRealtime.options.authHeaders[0].value);

assertEquals("proxy.port given for options must be equal to the one in in AblyRealtime before change", opts.proxy.port, ablyRealtime.options.proxy.port);
int origProxyPort = ablyRealtime.options.proxy.port;
opts.proxy.port = 9090;
assertNotEquals("proxy.port given for options should not be equal to the one in in AblyRealtime after change", opts.proxy.port, ablyRealtime.options.proxy.port);
assertEquals("proxy.port in AblyRealtime should be equal to original one after ClientOptions change", origProxyPort, ablyRealtime.options.proxy.port);

ablyRealtime.close();
} catch (AblyException e) {
e.printStackTrace();
fail();
}
}

}