diff --git a/android/src/main/java/io/ably/lib/platform/Platform.java b/android/src/main/java/io/ably/lib/platform/Platform.java index 1fe58f122..901b71be5 100644 --- a/android/src/main/java/io/ably/lib/platform/Platform.java +++ b/android/src/main/java/io/ably/lib/platform/Platform.java @@ -27,7 +27,7 @@ public void setAndroidContext(Context context) throws AblyException { Log.v(TAG, "setAndroidContext(): existing applicationContext is compatible with that being set"); return; } - throw AblyException.fromErrorInfo(new ErrorInfo("Incompatible application context set", 40000, 400)); + throw AblyException.fromErrorInfo(new ErrorInfo("Incompatible application context set", 400, 40000)); } else { Log.v(TAG, "setAndroidContext(): there was no existing applicationContext"); } diff --git a/android/src/main/java/io/ably/lib/push/ActivationContext.java b/android/src/main/java/io/ably/lib/push/ActivationContext.java index addb7d4eb..0a2224f07 100644 --- a/android/src/main/java/io/ably/lib/push/ActivationContext.java +++ b/android/src/main/java/io/ably/lib/push/ActivationContext.java @@ -73,7 +73,7 @@ AblyRest getAbly() throws AblyException { String deviceIdentityToken = getLocalDevice().deviceIdentityToken; if(deviceIdentityToken == null) { Log.e(TAG, "getAbly(): unable to create Ably instance using deviceIdentityToken"); - throw AblyException.fromErrorInfo(new ErrorInfo("Unable to get Ably library instance; no device identity token", 40000, 400)); + throw AblyException.fromErrorInfo(new ErrorInfo("Unable to get Ably library instance; no device identity token", 400, 40000)); } Log.v(TAG, "getAbly(): returning Ably instance using deviceIdentityToken"); // TODO: We need to persist Ably client options such as the environment with `deviceIdentityToken` and use these options during initialization. diff --git a/android/src/main/java/io/ably/lib/push/ActivationStateMachine.java b/android/src/main/java/io/ably/lib/push/ActivationStateMachine.java index afc07a1ea..ea604ee05 100644 --- a/android/src/main/java/io/ably/lib/push/ActivationStateMachine.java +++ b/android/src/main/java/io/ably/lib/push/ActivationStateMachine.java @@ -321,7 +321,7 @@ public void onSuccess(JsonObject response) { JsonObject deviceIdentityTokenJson = response.getAsJsonObject("deviceIdentityToken"); if(deviceIdentityTokenJson == null) { Log.e(TAG, "invalid device registration response (no deviceIdentityToken); deviceId = " + device.id); - machine.handleEvent(new ActivationStateMachine.GettingDeviceRegistrationFailed(new ErrorInfo("Invalid deviceIdentityToken in response", 40000, 400))); + machine.handleEvent(new ActivationStateMachine.GettingDeviceRegistrationFailed(new ErrorInfo("Invalid deviceIdentityToken in response", 400, 40000))); return; } JsonPrimitive responseClientIdJson = response.getAsJsonPrimitive("clientId"); diff --git a/android/src/main/java/io/ably/lib/push/Push.java b/android/src/main/java/io/ably/lib/push/Push.java index bce5da72e..66960ed2b 100644 --- a/android/src/main/java/io/ably/lib/push/Push.java +++ b/android/src/main/java/io/ably/lib/push/Push.java @@ -93,7 +93,7 @@ Context getApplicationContext() throws AblyException { Context applicationContext = rest.platform.getApplicationContext(); if(applicationContext == null) { Log.e(TAG, "getApplicationContext(): Unable to get application context; not set"); - throw AblyException.fromErrorInfo(new ErrorInfo("Unable to get application context; not set", 40000, 400)); + throw AblyException.fromErrorInfo(new ErrorInfo("Unable to get application context; not set", 400, 40000)); } return applicationContext; } diff --git a/lib/src/main/java/io/ably/lib/http/HttpAuth.java b/lib/src/main/java/io/ably/lib/http/HttpAuth.java index 1e6430ab7..4783f92ca 100644 --- a/lib/src/main/java/io/ably/lib/http/HttpAuth.java +++ b/lib/src/main/java/io/ably/lib/http/HttpAuth.java @@ -53,7 +53,7 @@ public static Map sortAuthenticateHeaders(Collection authe Map sortedHeaders = new HashMap<>(); for(String header : authenticateHeaders) { int delimiterIdx = header.indexOf(' '); - if(delimiterIdx == -1) { throw AblyException.fromErrorInfo(new ErrorInfo("Invalid authenticate header (no delimiter)", 40000, 400)); } + if(delimiterIdx == -1) { throw AblyException.fromErrorInfo(new ErrorInfo("Invalid authenticate header (no delimiter)", 400, 40000)); } String authType = header.substring(0, delimiterIdx).trim(); String authDetails = header.substring(delimiterIdx + 1).trim(); sortedHeaders.put(Type.parse(authType), authDetails); @@ -91,7 +91,7 @@ public void processAuthenticateHeaders(Map authenticateHeaders) th String authDetails = authenticateHeaders.get(type = prefType); if(authDetails == null) { Entry firstEntry = authenticateHeaders.entrySet().iterator().next(); - if(firstEntry == null) { throw AblyException.fromErrorInfo(new ErrorInfo("Invalid authenticate header (no entries)", 40000, 400)); } + if(firstEntry == null) { throw AblyException.fromErrorInfo(new ErrorInfo("Invalid authenticate header (no entries)", 400, 40000)); } type = firstEntry.getKey(); authDetails = firstEntry.getValue(); } diff --git a/lib/src/main/java/io/ably/lib/http/HttpCore.java b/lib/src/main/java/io/ably/lib/http/HttpCore.java index 2ba87d453..88f2dfb5e 100644 --- a/lib/src/main/java/io/ably/lib/http/HttpCore.java +++ b/lib/src/main/java/io/ably/lib/http/HttpCore.java @@ -93,17 +93,17 @@ public HttpCore(ClientOptions options, Auth auth, PlatformAgentProvider platform if (proxyOptions != null) { String proxyHost = proxyOptions.host; if (proxyHost == null) { - throw AblyException.fromErrorInfo(new ErrorInfo("Unable to configure proxy without proxy host", 40000, 400)); + throw AblyException.fromErrorInfo(new ErrorInfo("Unable to configure proxy without proxy host", 400, 40000)); } int proxyPort = proxyOptions.port; if (proxyPort == 0) { - throw AblyException.fromErrorInfo(new ErrorInfo("Unable to configure proxy without proxy port", 40000, 400)); + throw AblyException.fromErrorInfo(new ErrorInfo("Unable to configure proxy without proxy port", 400, 40000)); } String proxyUser = proxyOptions.username; if (proxyUser != null) { String proxyPassword = proxyOptions.password; if (proxyPassword == null) { - throw AblyException.fromErrorInfo(new ErrorInfo("Unable to configure proxy without proxy password", 40000, 400)); + throw AblyException.fromErrorInfo(new ErrorInfo("Unable to configure proxy without proxy password", 400, 40000)); } proxyAuth = new HttpAuth(proxyUser, proxyPassword, proxyOptions.prefAuthType); } diff --git a/lib/src/main/java/io/ably/lib/realtime/AblyRealtime.java b/lib/src/main/java/io/ably/lib/realtime/AblyRealtime.java index b991ed63b..553afa90a 100644 --- a/lib/src/main/java/io/ably/lib/realtime/AblyRealtime.java +++ b/lib/src/main/java/io/ably/lib/realtime/AblyRealtime.java @@ -211,7 +211,7 @@ public Channel get(final String channelName, final ChannelOptions channelOptions if (existingChannel != null) { if (channelOptions != null) { if (existingChannel.shouldReattachToSetOptions(channelOptions)) { - throw AblyException.fromErrorInfo(new ErrorInfo("Channels.get() cannot be used to set channel options that would cause the channel to reattach. Please, use Channel.setOptions() instead.", 40000, 400)); + throw AblyException.fromErrorInfo(new ErrorInfo("Channels.get() cannot be used to set channel options that would cause the channel to reattach. Please, use Channel.setOptions() instead.", 400, 40000)); } existingChannel.setOptions(channelOptions); } diff --git a/lib/src/main/java/io/ably/lib/realtime/ChannelBase.java b/lib/src/main/java/io/ably/lib/realtime/ChannelBase.java index fe68a481d..12feb0c61 100644 --- a/lib/src/main/java/io/ably/lib/realtime/ChannelBase.java +++ b/lib/src/main/java/io/ably/lib/realtime/ChannelBase.java @@ -1219,13 +1219,13 @@ static Param[] replacePlaceholderParams(Channel channel, Param[] placeholderPara if(KEY_UNTIL_ATTACH.equals(param.key)) { if("true".equalsIgnoreCase(param.value)) { if (channel.state != ChannelState.attached) { - throw AblyException.fromErrorInfo(new ErrorInfo("option untilAttach requires the channel to be attached", 40000, 400)); + throw AblyException.fromErrorInfo(new ErrorInfo("option untilAttach requires the channel to be attached", 400, 40000)); } params.add(new Param(KEY_FROM_SERIAL, channel.properties.attachSerial)); } else if(!"false".equalsIgnoreCase(param.value)) { - throw AblyException.fromErrorInfo(new ErrorInfo("option untilAttach is invalid. \"true\" or \"false\" expected", 40000, 400)); + throw AblyException.fromErrorInfo(new ErrorInfo("option untilAttach is invalid. \"true\" or \"false\" expected", 400, 40000)); } } else { diff --git a/lib/src/main/java/io/ably/lib/rest/Auth.java b/lib/src/main/java/io/ably/lib/rest/Auth.java index c16bcfdb8..b6dd2d123 100644 --- a/lib/src/main/java/io/ably/lib/rest/Auth.java +++ b/lib/src/main/java/io/ably/lib/rest/Auth.java @@ -185,7 +185,7 @@ public AuthOptions() {} */ public AuthOptions(String key) throws AblyException { if (key == null) { - throw AblyException.fromErrorInfo(new ErrorInfo("key string cannot be null", 40000, 400)); + throw AblyException.fromErrorInfo(new ErrorInfo("key string cannot be null", 400, 40000)); } if (key.isEmpty()) { throw new IllegalArgumentException("Key string cannot be empty"); diff --git a/lib/src/main/java/io/ably/lib/transport/ConnectionManager.java b/lib/src/main/java/io/ably/lib/transport/ConnectionManager.java index 4bd7ccf5a..aa5ffe336 100644 --- a/lib/src/main/java/io/ably/lib/transport/ConnectionManager.java +++ b/lib/src/main/java/io/ably/lib/transport/ConnectionManager.java @@ -944,7 +944,7 @@ private synchronized ConnectionStateChange setState(ITransport transport, StateI public void ping(final CompletionListener listener) { HeartbeatWaiter waiter = new HeartbeatWaiter(listener); if(currentState.state != ConnectionState.connected) { - waiter.onError(new ErrorInfo("Unable to ping service; not connected", 40000, 400)); + waiter.onError(new ErrorInfo("Unable to ping service; not connected", 400, 40000)); return; } synchronized(heartbeatWaiters) { @@ -1001,7 +1001,7 @@ public void run() { pending = clear(); } if(pending) { - onError(new ErrorInfo("Timed out waiting for heartbeat response", 50000, 500)); + onError(new ErrorInfo("Timed out waiting for heartbeat response", 500, 50000)); } else { onSuccess(); } diff --git a/lib/src/main/java/io/ably/lib/transport/Hosts.java b/lib/src/main/java/io/ably/lib/transport/Hosts.java index f9a82348f..361b983ea 100644 --- a/lib/src/main/java/io/ably/lib/transport/Hosts.java +++ b/lib/src/main/java/io/ably/lib/transport/Hosts.java @@ -49,10 +49,10 @@ public Hosts(final String primaryHost, final String defaultHost, final ClientOpt String[] tempFallbackHosts = options.fallbackHosts; if (options.fallbackHostsUseDefault) { if (options.fallbackHosts != null) { - throw AblyException.fromErrorInfo(new ErrorInfo("fallbackHosts and fallbackHostsUseDefault cannot both be set", 40000, 400)); + throw AblyException.fromErrorInfo(new ErrorInfo("fallbackHosts and fallbackHostsUseDefault cannot both be set", 400, 40000)); } if (options.port != 0 || options.tlsPort != 0) { - throw AblyException.fromErrorInfo(new ErrorInfo("fallbackHostsUseDefault cannot be set when port or tlsPort are set", 40000, 400)); + throw AblyException.fromErrorInfo(new ErrorInfo("fallbackHostsUseDefault cannot be set when port or tlsPort are set", 400, 40000)); } tempFallbackHosts = Defaults.HOST_FALLBACKS; } @@ -68,7 +68,7 @@ public Hosts(final String primaryHost, final String defaultHost, final ClientOpt if (options.environment != null) { /* TO3k2: It is never valid to provide both a restHost and environment value * TO3k3: It is never valid to provide both a realtimeHost and environment value */ - throw AblyException.fromErrorInfo(new ErrorInfo("cannot set both restHost/realtimeHost and environment options", 40000, 400)); + throw AblyException.fromErrorInfo(new ErrorInfo("cannot set both restHost/realtimeHost and environment options", 400, 40000)); } } else { this.primaryHost = isProduction ? defaultHost : options.environment + "-" + defaultHost; diff --git a/lib/src/main/java/io/ably/lib/types/ErrorInfo.java b/lib/src/main/java/io/ably/lib/types/ErrorInfo.java index 949091def..2b9f9f147 100644 --- a/lib/src/main/java/io/ably/lib/types/ErrorInfo.java +++ b/lib/src/main/java/io/ably/lib/types/ErrorInfo.java @@ -156,7 +156,7 @@ else if(throwable instanceof IOException) { errorInfo = new ErrorInfo(throwable.getLocalizedMessage(), 500, 50000); } else { - errorInfo = new ErrorInfo("Unexpected exception: " + throwable.getLocalizedMessage(), 50000, 500); + errorInfo = new ErrorInfo("Unexpected exception: " + throwable.getLocalizedMessage(), 500, 50000); } return errorInfo; diff --git a/lib/src/test/java/io/ably/lib/transport/ConnectionManagerPingErrorTest.java b/lib/src/test/java/io/ably/lib/transport/ConnectionManagerPingErrorTest.java new file mode 100644 index 000000000..a3b17429d --- /dev/null +++ b/lib/src/test/java/io/ably/lib/transport/ConnectionManagerPingErrorTest.java @@ -0,0 +1,40 @@ +package io.ably.lib.transport; + +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.notNullValue; +import static org.junit.Assert.assertThat; + +import io.ably.lib.realtime.AblyRealtime; +import io.ably.lib.realtime.CompletionListener; +import io.ably.lib.types.AblyException; +import io.ably.lib.types.ClientOptions; +import io.ably.lib.types.ErrorInfo; +import java.util.concurrent.atomic.AtomicReference; +import org.junit.Test; + +public class ConnectionManagerPingErrorTest { + + /** + * Pinging while not connected reports Ably code 40000 with HTTP status code 400. + */ + @Test + public void ping_when_not_connected_error_codes() throws AblyException { + ClientOptions options = new ClientOptions("appid.keyid:keysecret"); + options.autoConnect = false; + AtomicReference error = new AtomicReference<>(); + try (AblyRealtime ably = new AblyRealtime(options)) { + ably.connection.ping(new CompletionListener() { + @Override + public void onSuccess() {} + + @Override + public void onError(ErrorInfo reason) { + error.set(reason); + } + }); + } + assertThat(error.get(), is(notNullValue())); + assertThat(error.get().code, is(40000)); + assertThat(error.get().statusCode, is(400)); + } +} diff --git a/lib/src/test/java/io/ably/lib/transport/HostsTest.java b/lib/src/test/java/io/ably/lib/transport/HostsTest.java index 2e7750b93..ac5e1cf26 100644 --- a/lib/src/test/java/io/ably/lib/transport/HostsTest.java +++ b/lib/src/test/java/io/ably/lib/transport/HostsTest.java @@ -6,6 +6,7 @@ import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.nullValue; import static org.junit.Assert.assertThat; +import static org.junit.Assert.fail; import io.ably.lib.types.AblyException; import io.ably.lib.types.ClientOptions; @@ -46,6 +47,21 @@ public void hosts_host_and_environment() throws AblyException { new Hosts("overridden.ably.io", Defaults.HOST_REALTIME, options); } + /** + * Configuration errors carry Ably code 40000 and HTTP status code 400. + */ + @Test + public void hosts_invalid_options_error_codes() { + options.environment = "myenv"; + try { + new Hosts("overridden.ably.io", Defaults.HOST_REALTIME, options); + fail("expected AblyException"); + } catch (AblyException e) { + assertThat(e.errorInfo.code, is(40000)); + assertThat(e.errorInfo.statusCode, is(400)); + } + } + /** * Expect a null, when we provide empty array of fallback hosts */