Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update websocket dependency #520

Merged
merged 17 commits into from
Nov 15, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
15a0552
Add Maven Central repository
paddybyers Nov 1, 2019
4b512ef
Update java-websocket dependency
paddybyers Nov 1, 2019
fadd232
HttpUtils: fix encodeURIComponent() wrt handling of space
paddybyers Nov 1, 2019
b5790f6
RawProtocolListener: add onRawConnectRequested()
paddybyers Nov 1, 2019
6bb4a16
ConnectionManager: ensure that a connection attempt that is interrupt…
paddybyers Nov 1, 2019
a8d5596
Auth.timeDelta: make this instance-wide, not static, to avoid test in…
paddybyers Nov 4, 2019
c1fead9
Defaults: correct disconnect retry delay to 15s
paddybyers Nov 8, 2019
f96f86a
Helpers.ConnectionWaiter: add some logging
paddybyers Nov 8, 2019
9962924
WebSocketTransport: ensure that async events are propagated in a sepa…
paddybyers Nov 8, 2019
24b4930
ConnectionManager: fix connection retry regression
paddybyers Nov 8, 2019
31b38c1
RestSuite: revert accidentially-committed disabled tests
paddybyers Nov 8, 2019
e71c7eb
RealtimeAuthTest: don't overwrite static defaults, and increase test …
paddybyers Nov 8, 2019
9bd0885
WebsocketTransport: ensure that the timer exists always, not only whe…
paddybyers Nov 11, 2019
d073edb
WebsocketTransport: stop using deprecated setSocket() for SSL
paddybyers Nov 11, 2019
d179cfa
ConnectionManagerTest: avoid use of race-prone ConnectionManagerWaite…
paddybyers Nov 15, 2019
6a81179
RestPushTest: @Ignore list_channels test, which fails randomly due to…
paddybyers Nov 15, 2019
12ba625
ConnectionManagerTest: @Ignore tests that are not valid tests for the…
paddybyers Nov 15, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions common.gradle
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
repositories {
jcenter()
mavenCentral()
}

group = 'io.ably'
Expand Down
2 changes: 1 addition & 1 deletion dependencies.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// in java/build.gradle and android/build.gradle for maven.
dependencies {
implementation 'org.msgpack:msgpack-core:0.8.11'
implementation 'io.ably:Java-WebSocket:1.3.1'
implementation 'org.java-websocket:Java-WebSocket:1.4.0'
implementation 'com.google.code.gson:gson:2.5'
testImplementation 'org.hamcrest:hamcrest-all:1.3'
testImplementation 'junit:junit:4.12'
Expand Down
1 change: 1 addition & 0 deletions lib/src/main/java/io/ably/lib/debug/DebugOptions.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

public class DebugOptions extends ClientOptions {
public interface RawProtocolListener {
public void onRawConnectRequested(String url);
public void onRawConnect(String url);
public void onRawMessageSend(ProtocolMessage message);
public void onRawMessageRecv(ProtocolMessage message);
Expand Down
37 changes: 24 additions & 13 deletions lib/src/main/java/io/ably/lib/http/HttpUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
Expand Down Expand Up @@ -138,19 +139,29 @@ public static String getParam(Param[] params, String key) {
return result;
}

public static String encodeURIComponent(String input) {
try {
return URLEncoder.encode(input, "UTF-8")
.replaceAll(" ", "%20")
.replaceAll("!", "%21")
.replaceAll("'", "%27")
.replaceAll("\\(", "%28")
.replaceAll("\\)", "%29")
.replaceAll("\\+", "%2B")
.replaceAll("\\:", "%3A")
.replaceAll("~", "%7E");
} catch (UnsupportedEncodingException e) {}
return null;
/* copied from https://stackoverflow.com/a/52378025 */
private static final String HEX = "0123456789ABCDEF";

public static String encodeURIComponent(String str) {
paddybyers marked this conversation as resolved.
Show resolved Hide resolved
if (str == null) {
return null;
}

byte[] bytes = str.getBytes(StandardCharsets.UTF_8);
StringBuilder builder = new StringBuilder(bytes.length);

for (byte c : bytes) {
if (c >= 'a' ? c <= 'z' || c == '~' :
c >= 'A' ? c <= 'Z' || c == '_' :
c >= '0' ? c <= '9' : c == '-' || c == '.')
builder.append((char)c);
else
builder.append('%')
.append(HEX.charAt(c >> 4 & 0xf))
.append(HEX.charAt(c & 0xf));
}

return builder.toString();
}

private static void appendParams(StringBuilder uri, Param[] params) {
Expand Down
12 changes: 6 additions & 6 deletions lib/src/main/java/io/ably/lib/rest/Auth.java
Original file line number Diff line number Diff line change
Expand Up @@ -964,9 +964,9 @@ private TokenDetails assertValidToken(TokenParams params, AuthOptions options, b
return tokenDetails;
}

private static boolean tokenValid(TokenDetails tokenDetails) {
private boolean tokenValid(TokenDetails tokenDetails) {
paddybyers marked this conversation as resolved.
Show resolved Hide resolved
/* RSA4b1: only perform a local check for token validity if we have time sync with the server */
return (timeDelta == Long.MAX_VALUE) || (tokenDetails.expires > Auth.serverTimestamp());
return (timeDelta == Long.MAX_VALUE) || (tokenDetails.expires > serverTimestamp());
}

/**
Expand Down Expand Up @@ -1078,7 +1078,7 @@ public String checkClientId(BaseMessage msg, boolean allowNullClientId, boolean
/**
* Using time delta obtained before guess current server time
*/
public static long serverTimestamp() {
public long serverTimestamp() {
long clientTime = timestamp();
long delta = timeDelta;
return delta != Long.MAX_VALUE ? clientTime + timeDelta : clientTime;
Expand All @@ -1097,18 +1097,18 @@ public static long serverTimestamp() {
/**
* Time delta is server time minus client time, in milliseconds, MAX_VALUE if not obtained yet
*/
private static long timeDelta = Long.MAX_VALUE;
private long timeDelta = Long.MAX_VALUE;
/**
* Time delta between System.nanoTime() and System.currentTimeMillis. If it changes significantly it
* suggests device time/date has changed
*/
private static long nanoTimeDelta = System.currentTimeMillis() - System.nanoTime()/(1000*1000);
private long nanoTimeDelta = System.currentTimeMillis() - System.nanoTime()/(1000*1000);

public static final String WILDCARD_CLIENTID = "*";
/**
* For testing purposes we need method to clear cached timeDelta
*/
public static void clearCachedServerTime() {
public void clearCachedServerTime() {
timeDelta = Long.MAX_VALUE;
}
}
86 changes: 55 additions & 31 deletions lib/src/main/java/io/ably/lib/transport/ConnectionManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -53,16 +53,18 @@ public static class StateIndication {
final ErrorInfo reason;
final String fallback;
final String currentHost;
final boolean retryImmediately;

public StateIndication(ConnectionState state, ErrorInfo reason) {
this(state, reason, null, null);
this(state, reason, null, null, false);
}

public StateIndication(ConnectionState state, ErrorInfo reason, String fallback, String currentHost) {
public StateIndication(ConnectionState state, ErrorInfo reason, String fallback, String currentHost, boolean retryImmediately) {
this.state = state;
this.reason = reason;
this.fallback = fallback;
this.currentHost = currentHost;
this.retryImmediately = retryImmediately;
}
}

Expand Down Expand Up @@ -375,34 +377,44 @@ public void run() {
*/
public void onAuthUpdated(String token, boolean waitForResponse) throws AblyException {
ConnectionWaiter waiter = new ConnectionWaiter();
if (state.state == ConnectionState.connected) {
/* (RTC8a) If the connection is in the CONNECTED state and
* auth.authorize is called or Ably requests a re-authentication
* (see RTN22), the client must obtain a new token, then send an
* AUTH ProtocolMessage to Ably with an auth attribute
* containing an AuthDetails object with the token string. */
try {
ProtocolMessage msg = new ProtocolMessage(ProtocolMessage.Action.auth);
msg.auth = new ProtocolMessage.AuthDetails(token);
send(msg, false, null);
} catch (AblyException e) {
/* The send failed. Close the transport; if a subsequent
* reconnect succeeds, it will be with the new token. */
Log.v(TAG, "onAuthUpdated: closing transport after send failure");
transport.close(/*sendDisconnect=*/false);
}
} else {
if (state.state == ConnectionState.connecting) {
switch(state.state) {
case connected:
/* (RTC8a) If the connection is in the CONNECTED state and
* auth.authorize is called or Ably requests a re-authentication
* (see RTN22), the client must obtain a new token, then send an
* AUTH ProtocolMessage to Ably with an auth attribute
* containing an AuthDetails object with the token string. */
try {
ProtocolMessage msg = new ProtocolMessage(ProtocolMessage.Action.auth);
msg.auth = new ProtocolMessage.AuthDetails(token);
send(msg, false, null);
} catch (AblyException e) {
/* The send failed. Close the transport; if a subsequent
* reconnect succeeds, it will be with the new token. */
Log.v(TAG, "onAuthUpdated: closing transport after send failure");
transport.close(/*sendDisconnect=*/false);
}
break;

case connecting:
/* Close the connecting transport. */
Log.v(TAG, "onAuthUpdated: closing connecting transport");
transport.close(/*sendDisconnect=*/false);
}
/* Start a new connection attempt. */
connect();
clearTransport();
/* request a state change that triggers an immediate retry */
Log.v(TAG, "onAuthUpdated: requesting immediate new connection attempt");
ErrorInfo disconnectError = new ErrorInfo("Aborting incomplete connection with superseded auth params", 503, 80003);
requestState(new StateIndication(ConnectionState.disconnected, disconnectError, null, null, true));
break;

default:
/* Start a new connection attempt. */
connect();
break;
}

if(!waitForResponse)
if(!waitForResponse) {
return;
}

/* Wait for a state transition into anything other than connecting or
* disconnected. Note that this includes the case that the connection
Expand Down Expand Up @@ -676,7 +688,7 @@ private StateIndication handleStateRequest() {
if(connectImpl(transitionState)) {
return transitionState;
} else {
return new StateIndication(ConnectionState.failed, new ErrorInfo("Connection failed; no host available", 404, 80000), null, requestedState.currentHost);
return new StateIndication(ConnectionState.failed, new ErrorInfo("Connection failed; no host available", 404, 80000), null, requestedState.currentHost, false);
}
}
/* no other requests can move from a terminal state */
Expand Down Expand Up @@ -738,7 +750,11 @@ private void handleStateChange(StateIndication stateChange) {
}
switch(state.state) {
case connecting:
stateChange = checkSuspend(stateChange);
if(stateChange.retryImmediately && !suppressRetry) {
requestState(ConnectionState.connecting);
} else {
stateChange = checkSuspend(stateChange);
}
pendingConnect = null;
break;
case closing:
Expand Down Expand Up @@ -798,7 +814,7 @@ private StateIndication checkSuspend(StateIndication stateChange) {
String hostFallback = hosts.getFallback(pendingConnect.host);
if (hostFallback != null) {
Log.v(TAG, "checkSuspend: fallback to " + hostFallback);
requestState(new StateIndication(ConnectionState.connecting, null, hostFallback, pendingConnect.host));
requestState(new StateIndication(ConnectionState.connecting, null, hostFallback, pendingConnect.host, false));
/* returning null ensures we stay in the connecting state */
return null;
}
Expand Down Expand Up @@ -943,7 +959,7 @@ public synchronized void onTransportUnavailable(ITransport transport, TransportP
Log.i(TAG, "onTransportUnavailable: disconnected: " + reason.message);
}
ably.auth.onAuthError(reason);
requestState(new StateIndication(state, reason, null, transport.getHost()));
requestState(new StateIndication(state, reason, null, transport.getHost(), false));
this.transport = null;
}

Expand Down Expand Up @@ -987,9 +1003,13 @@ private boolean connectImpl(StateIndication request) {
oldTransport = this.transport;
this.transport = transport;
}
if (oldTransport != null)
if (oldTransport != null) {
oldTransport.abort(REASON_TIMEDOUT);
}
transport.connect(this);
if(protocolListener != null) {
protocolListener.onRawConnectRequested(transport.getURL());
}
return true;
}

Expand Down Expand Up @@ -1300,10 +1320,14 @@ void disconnectAndSuppressRetries() {
* internal
******************/

private boolean isTokenError(ErrorInfo err) {
return (err.code >= 40140) && (err.code < 40150);
}

private boolean isFatalError(ErrorInfo err) {
if(err.code != 0) {
/* token errors are assumed to be recoverable */
if((err.code >= 40140) && (err.code < 40150)) { return false; }
if(isTokenError(err)) { return false; }
/* 400 codes assumed to be fatal */
if((err.code >= 40000) && (err.code < 50000)) { return true; }
}
Expand Down
2 changes: 1 addition & 1 deletion lib/src/main/java/io/ably/lib/transport/Defaults.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public class Defaults {

/* Timeouts */
public static int TIMEOUT_CONNECT = 15000;
public static int TIMEOUT_DISCONNECT = 30000;
public static int TIMEOUT_DISCONNECT = 15000;
public static int TIMEOUT_CHANNEL_RETRY = 15000;

/* TO313 */
Expand Down