[Java] Add pings and server timeout (#3027)
This commit is contained in:
parent
be4fe6c6f9
commit
2ee351786f
|
|
@ -6,11 +6,15 @@ package com.microsoft.signalr;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Date;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Timer;
|
||||||
|
import java.util.TimerTask;
|
||||||
import java.util.concurrent.*;
|
import java.util.concurrent.*;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
import java.util.concurrent.atomic.AtomicLong;
|
||||||
import java.util.concurrent.locks.Lock;
|
import java.util.concurrent.locks.Lock;
|
||||||
import java.util.concurrent.locks.ReentrantLock;
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
@ -39,9 +43,36 @@ public class HubConnection {
|
||||||
private ConnectionState connectionState = null;
|
private ConnectionState connectionState = null;
|
||||||
private HttpClient httpClient;
|
private HttpClient httpClient;
|
||||||
private String stopError;
|
private String stopError;
|
||||||
|
private Timer pingTimer = null;
|
||||||
|
private AtomicLong nextServerTimeout = new AtomicLong();
|
||||||
|
private AtomicLong nextPingActivation = new AtomicLong();
|
||||||
|
private Duration keepAliveInterval = Duration.ofSeconds(15);
|
||||||
|
private Duration serverTimeout = Duration.ofSeconds(30);
|
||||||
|
private Duration tickRate = Duration.ofSeconds(1);
|
||||||
private CompletableFuture<Void> handshakeResponseFuture;
|
private CompletableFuture<Void> handshakeResponseFuture;
|
||||||
private Duration handshakeResponseTimeout = Duration.ofSeconds(15);
|
private Duration handshakeResponseTimeout = Duration.ofSeconds(15);
|
||||||
|
|
||||||
|
public void setServerTimeout(Duration serverTimeout) {
|
||||||
|
this.serverTimeout = serverTimeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Duration getServerTimeout() {
|
||||||
|
return this.serverTimeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setKeepAliveInterval(Duration keepAliveInterval) {
|
||||||
|
this.keepAliveInterval = keepAliveInterval;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Duration getKeepAliveInterval() {
|
||||||
|
return this.keepAliveInterval;
|
||||||
|
}
|
||||||
|
|
||||||
|
// For testing purposes
|
||||||
|
void setTickRate(Duration tickRate) {
|
||||||
|
this.tickRate = tickRate;
|
||||||
|
}
|
||||||
|
|
||||||
HubConnection(String url, Transport transport, boolean skipNegotiate, Logger logger, HttpClient httpClient, Single<String> accessTokenProvider, Duration handshakeResponseTimeout) {
|
HubConnection(String url, Transport transport, boolean skipNegotiate, Logger logger, HttpClient httpClient, Single<String> accessTokenProvider, Duration handshakeResponseTimeout) {
|
||||||
if (url == null || url.isEmpty()) {
|
if (url == null || url.isEmpty()) {
|
||||||
throw new IllegalArgumentException("A valid url is required.");
|
throw new IllegalArgumentException("A valid url is required.");
|
||||||
|
|
@ -79,6 +110,7 @@ public class HubConnection {
|
||||||
this.skipNegotiate = skipNegotiate;
|
this.skipNegotiate = skipNegotiate;
|
||||||
|
|
||||||
this.callback = (payload) -> {
|
this.callback = (payload) -> {
|
||||||
|
resetServerTimeout();
|
||||||
if (!handshakeReceived) {
|
if (!handshakeReceived) {
|
||||||
int handshakeLength = payload.indexOf(RECORD_SEPARATOR) + 1;
|
int handshakeLength = payload.indexOf(RECORD_SEPARATOR) + 1;
|
||||||
String handshakeResponseString = payload.substring(0, handshakeLength - 1);
|
String handshakeResponseString = payload.substring(0, handshakeLength - 1);
|
||||||
|
|
@ -245,6 +277,29 @@ public class HubConnection {
|
||||||
hubConnectionState = HubConnectionState.CONNECTED;
|
hubConnectionState = HubConnectionState.CONNECTED;
|
||||||
connectionState = new ConnectionState(this);
|
connectionState = new ConnectionState(this);
|
||||||
logger.log(LogLevel.Information, "HubConnection started.");
|
logger.log(LogLevel.Information, "HubConnection started.");
|
||||||
|
|
||||||
|
resetServerTimeout();
|
||||||
|
this.pingTimer = new Timer();
|
||||||
|
this.pingTimer.schedule(new TimerTask() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
try {
|
||||||
|
if (System.currentTimeMillis() > nextServerTimeout.get()) {
|
||||||
|
stop("Server timeout elapsed without receiving a message from the server.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (System.currentTimeMillis() > nextPingActivation.get()) {
|
||||||
|
sendHubMessage(PingMessage.getInstance());
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.log(LogLevel.Warning, String.format("Error sending ping: %s", e.getMessage()));
|
||||||
|
// The connection is probably in a bad or closed state now, cleanup the timer so
|
||||||
|
// it stops triggering
|
||||||
|
pingTimer.cancel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, new Date(0), tickRate.toMillis());
|
||||||
} finally {
|
} finally {
|
||||||
hubConnectionStateLock.unlock();
|
hubConnectionStateLock.unlock();
|
||||||
}
|
}
|
||||||
|
|
@ -399,11 +454,21 @@ public class HubConnection {
|
||||||
private void sendHubMessage(HubMessage message) throws Exception {
|
private void sendHubMessage(HubMessage message) throws Exception {
|
||||||
String serializedMessage = protocol.writeMessage(message);
|
String serializedMessage = protocol.writeMessage(message);
|
||||||
if (message.getMessageType() == HubMessageType.INVOCATION) {
|
if (message.getMessageType() == HubMessageType.INVOCATION) {
|
||||||
logger.log(LogLevel.Debug, "Sending %d message '%s'.", message.getMessageType().value, ((InvocationMessage)message).getInvocationId());
|
logger.log(LogLevel.Debug, "Sending %s message '%s'.", message.getMessageType().name(), ((InvocationMessage)message).getInvocationId());
|
||||||
} else {
|
} else {
|
||||||
logger.log(LogLevel.Debug, "Sending %d message.", message.getMessageType().value);
|
logger.log(LogLevel.Debug, "Sending %s message.", message.getMessageType().name());
|
||||||
}
|
}
|
||||||
transport.send(serializedMessage);
|
transport.send(serializedMessage);
|
||||||
|
|
||||||
|
resetKeepAlive();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void resetServerTimeout() {
|
||||||
|
this.nextServerTimeout.set(System.currentTimeMillis() + serverTimeout.toMillis());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void resetKeepAlive() {
|
||||||
|
this.nextPingActivation.set(System.currentTimeMillis() + keepAliveInterval.toMillis());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,8 @@ package com.microsoft.signalr;
|
||||||
|
|
||||||
class PingMessage extends HubMessage
|
class PingMessage extends HubMessage
|
||||||
{
|
{
|
||||||
|
int type = HubMessageType.PING.value;
|
||||||
|
|
||||||
private static PingMessage instance = new PingMessage();
|
private static PingMessage instance = new PingMessage();
|
||||||
|
|
||||||
private PingMessage()
|
private PingMessage()
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@ class HubConnectionTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void transportCloseTriggersStopInHubConnection() throws Exception {
|
public void transportCloseTriggersStopInHubConnection() throws Exception {
|
||||||
MockTransport mockTransport = new MockTransport(true);
|
MockTransport mockTransport = new MockTransport();
|
||||||
HubConnection hubConnection = TestUtils.createHubConnection("http://example.com", mockTransport);
|
HubConnection hubConnection = TestUtils.createHubConnection("http://example.com", mockTransport);
|
||||||
hubConnection.start().blockingAwait(1000, TimeUnit.MILLISECONDS);
|
hubConnection.start().blockingAwait(1000, TimeUnit.MILLISECONDS);
|
||||||
assertEquals(HubConnectionState.CONNECTED, hubConnection.getConnectionState());
|
assertEquals(HubConnectionState.CONNECTED, hubConnection.getConnectionState());
|
||||||
|
|
@ -46,7 +46,7 @@ class HubConnectionTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void transportCloseWithErrorTriggersStopInHubConnection() throws Exception {
|
public void transportCloseWithErrorTriggersStopInHubConnection() throws Exception {
|
||||||
MockTransport mockTransport = new MockTransport(true);
|
MockTransport mockTransport = new MockTransport();
|
||||||
AtomicReference<String> message = new AtomicReference<>();
|
AtomicReference<String> message = new AtomicReference<>();
|
||||||
HubConnection hubConnection = TestUtils.createHubConnection("http://example.com", mockTransport);
|
HubConnection hubConnection = TestUtils.createHubConnection("http://example.com", mockTransport);
|
||||||
String errorMessage = "Example transport error.";
|
String errorMessage = "Example transport error.";
|
||||||
|
|
@ -63,7 +63,7 @@ class HubConnectionTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void checkHubConnectionStateNoHandShakeResponse() {
|
public void checkHubConnectionStateNoHandShakeResponse() {
|
||||||
MockTransport mockTransport = new MockTransport();
|
MockTransport mockTransport = new MockTransport(false);
|
||||||
HubConnection hubConnection = HubConnectionBuilder.create("http://example.com")
|
HubConnection hubConnection = HubConnectionBuilder.create("http://example.com")
|
||||||
.withTransport(mockTransport)
|
.withTransport(mockTransport)
|
||||||
.withHttpClient(new TestHttpClient())
|
.withHttpClient(new TestHttpClient())
|
||||||
|
|
@ -79,7 +79,7 @@ class HubConnectionTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void constructHubConnectionWithHttpConnectionOptions() {
|
public void constructHubConnectionWithHttpConnectionOptions() {
|
||||||
Transport mockTransport = new MockTransport(true);
|
Transport mockTransport = new MockTransport();
|
||||||
HubConnection hubConnection = TestUtils.createHubConnection("http://example.com", mockTransport);
|
HubConnection hubConnection = TestUtils.createHubConnection("http://example.com", mockTransport);
|
||||||
|
|
||||||
hubConnection.start();
|
hubConnection.start();
|
||||||
|
|
@ -95,7 +95,6 @@ class HubConnectionTest {
|
||||||
HubConnection hubConnection = TestUtils.createHubConnection("http://example.com", mockTransport);
|
HubConnection hubConnection = TestUtils.createHubConnection("http://example.com", mockTransport);
|
||||||
|
|
||||||
hubConnection.start();
|
hubConnection.start();
|
||||||
mockTransport.receiveMessage("{}" + RECORD_SEPARATOR);
|
|
||||||
|
|
||||||
assertEquals(HubConnectionState.CONNECTED, hubConnection.getConnectionState());
|
assertEquals(HubConnectionState.CONNECTED, hubConnection.getConnectionState());
|
||||||
|
|
||||||
|
|
@ -106,7 +105,7 @@ class HubConnectionTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void invalidHandShakeResponse() throws Exception {
|
public void invalidHandShakeResponse() throws Exception {
|
||||||
MockTransport mockTransport = new MockTransport();
|
MockTransport mockTransport = new MockTransport(false);
|
||||||
HubConnection hubConnection = TestUtils.createHubConnection("http://example.com", mockTransport);
|
HubConnection hubConnection = TestUtils.createHubConnection("http://example.com", mockTransport);
|
||||||
|
|
||||||
hubConnection.start();
|
hubConnection.start();
|
||||||
|
|
@ -118,7 +117,7 @@ class HubConnectionTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void hubConnectionReceiveHandshakeResponseWithError() {
|
public void hubConnectionReceiveHandshakeResponseWithError() {
|
||||||
MockTransport mockTransport = new MockTransport();
|
MockTransport mockTransport = new MockTransport(false);
|
||||||
HubConnection hubConnection = TestUtils.createHubConnection("http://example.com", mockTransport);
|
HubConnection hubConnection = TestUtils.createHubConnection("http://example.com", mockTransport);
|
||||||
|
|
||||||
hubConnection.start();
|
hubConnection.start();
|
||||||
|
|
@ -145,7 +144,6 @@ class HubConnectionTest {
|
||||||
|
|
||||||
assertEquals(expectedHanshakeRequest, message);
|
assertEquals(expectedHanshakeRequest, message);
|
||||||
|
|
||||||
mockTransport.receiveMessage("{}" + RECORD_SEPARATOR);
|
|
||||||
mockTransport.receiveMessage("{\"type\":1,\"target\":\"inc\",\"arguments\":[]}" + RECORD_SEPARATOR);
|
mockTransport.receiveMessage("{\"type\":1,\"target\":\"inc\",\"arguments\":[]}" + RECORD_SEPARATOR);
|
||||||
|
|
||||||
// Confirming that our handler was called and that the counter property was incremented.
|
// Confirming that our handler was called and that the counter property was incremented.
|
||||||
|
|
@ -169,7 +167,6 @@ class HubConnectionTest {
|
||||||
|
|
||||||
assertEquals(expectedHanshakeRequest, message);
|
assertEquals(expectedHanshakeRequest, message);
|
||||||
|
|
||||||
mockTransport.receiveMessage("{}" + RECORD_SEPARATOR);
|
|
||||||
mockTransport.receiveMessage("{\"type\":1,\"target\":\"inc\",\"arguments\":[]}" + RECORD_SEPARATOR);
|
mockTransport.receiveMessage("{\"type\":1,\"target\":\"inc\",\"arguments\":[]}" + RECORD_SEPARATOR);
|
||||||
|
|
||||||
// Confirming that our handler was called and that the counter property was incremented.
|
// Confirming that our handler was called and that the counter property was incremented.
|
||||||
|
|
@ -197,7 +194,6 @@ class HubConnectionTest {
|
||||||
|
|
||||||
assertEquals(expectedHanshakeRequest, message);
|
assertEquals(expectedHanshakeRequest, message);
|
||||||
|
|
||||||
mockTransport.receiveMessage("{}" + RECORD_SEPARATOR);
|
|
||||||
mockTransport.receiveMessage("{\"type\":1,\"target\":\"inc\",\"arguments\":[]}" + RECORD_SEPARATOR);
|
mockTransport.receiveMessage("{\"type\":1,\"target\":\"inc\",\"arguments\":[]}" + RECORD_SEPARATOR);
|
||||||
|
|
||||||
// Confirming that the handler was removed.
|
// Confirming that the handler was removed.
|
||||||
|
|
@ -223,7 +219,6 @@ class HubConnectionTest {
|
||||||
|
|
||||||
assertEquals(expectedHanshakeRequest, message);
|
assertEquals(expectedHanshakeRequest, message);
|
||||||
|
|
||||||
mockTransport.receiveMessage("{}" + RECORD_SEPARATOR);
|
|
||||||
mockTransport.receiveMessage("{\"type\":1,\"target\":\"inc\",\"arguments\":[]}" + RECORD_SEPARATOR);
|
mockTransport.receiveMessage("{\"type\":1,\"target\":\"inc\",\"arguments\":[]}" + RECORD_SEPARATOR);
|
||||||
|
|
||||||
assertEquals(Double.valueOf(3), value.get());
|
assertEquals(Double.valueOf(3), value.get());
|
||||||
|
|
@ -253,7 +248,6 @@ class HubConnectionTest {
|
||||||
|
|
||||||
assertEquals(expectedHanshakeRequest, message);
|
assertEquals(expectedHanshakeRequest, message);
|
||||||
|
|
||||||
mockTransport.receiveMessage("{}" + RECORD_SEPARATOR);
|
|
||||||
mockTransport.receiveMessage("{\"type\":1,\"target\":\"inc\",\"arguments\":[]}" + RECORD_SEPARATOR);
|
mockTransport.receiveMessage("{\"type\":1,\"target\":\"inc\",\"arguments\":[]}" + RECORD_SEPARATOR);
|
||||||
|
|
||||||
// Confirming that our handler was called and that the counter property was incremented.
|
// Confirming that our handler was called and that the counter property was incremented.
|
||||||
|
|
@ -286,7 +280,6 @@ class HubConnectionTest {
|
||||||
|
|
||||||
assertEquals(expectedHanshakeRequest, message);
|
assertEquals(expectedHanshakeRequest, message);
|
||||||
|
|
||||||
mockTransport.receiveMessage("{}" + RECORD_SEPARATOR);
|
|
||||||
mockTransport.receiveMessage("{\"type\":1,\"target\":\"inc\",\"arguments\":[]}" + RECORD_SEPARATOR);
|
mockTransport.receiveMessage("{\"type\":1,\"target\":\"inc\",\"arguments\":[]}" + RECORD_SEPARATOR);
|
||||||
|
|
||||||
// Confirming that our handler was called and that the counter property was incremented.
|
// Confirming that our handler was called and that the counter property was incremented.
|
||||||
|
|
@ -322,7 +315,6 @@ class HubConnectionTest {
|
||||||
|
|
||||||
assertEquals(expectedHanshakeRequest, message);
|
assertEquals(expectedHanshakeRequest, message);
|
||||||
|
|
||||||
mockTransport.receiveMessage("{}" + RECORD_SEPARATOR);
|
|
||||||
mockTransport.receiveMessage("{\"type\":1,\"target\":\"inc\",\"arguments\":[]}" + RECORD_SEPARATOR);
|
mockTransport.receiveMessage("{\"type\":1,\"target\":\"inc\",\"arguments\":[]}" + RECORD_SEPARATOR);
|
||||||
// Confirming that our handler was called and that the counter property was incremented.
|
// Confirming that our handler was called and that the counter property was incremented.
|
||||||
assertEquals(Double.valueOf(3), value.get());
|
assertEquals(Double.valueOf(3), value.get());
|
||||||
|
|
@ -346,7 +338,6 @@ class HubConnectionTest {
|
||||||
assertEquals(Double.valueOf(0), value.get());
|
assertEquals(Double.valueOf(0), value.get());
|
||||||
|
|
||||||
hubConnection.start();
|
hubConnection.start();
|
||||||
mockTransport.receiveMessage("{}" + RECORD_SEPARATOR);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
mockTransport.receiveMessage("{\"type\":1,\"target\":\"inc\",\"arguments\":[]}" + RECORD_SEPARATOR);
|
mockTransport.receiveMessage("{\"type\":1,\"target\":\"inc\",\"arguments\":[]}" + RECORD_SEPARATOR);
|
||||||
|
|
@ -371,7 +362,6 @@ class HubConnectionTest {
|
||||||
|
|
||||||
assertEquals(Double.valueOf(0), value.get());
|
assertEquals(Double.valueOf(0), value.get());
|
||||||
hubConnection.start();
|
hubConnection.start();
|
||||||
mockTransport.receiveMessage("{}" + RECORD_SEPARATOR);
|
|
||||||
mockTransport.receiveMessage("{\"type\":1,\"target\":\"add\",\"arguments\":[12]}" + RECORD_SEPARATOR);
|
mockTransport.receiveMessage("{\"type\":1,\"target\":\"add\",\"arguments\":[12]}" + RECORD_SEPARATOR);
|
||||||
|
|
||||||
// Confirming that our handler was called and the correct message was passed in.
|
// Confirming that our handler was called and the correct message was passed in.
|
||||||
|
|
@ -384,7 +374,6 @@ class HubConnectionTest {
|
||||||
HubConnection hubConnection = TestUtils.createHubConnection("http://example.com", mockTransport);
|
HubConnection hubConnection = TestUtils.createHubConnection("http://example.com", mockTransport);
|
||||||
|
|
||||||
hubConnection.start();
|
hubConnection.start();
|
||||||
mockTransport.receiveMessage("{}" + RECORD_SEPARATOR);
|
|
||||||
|
|
||||||
AtomicBoolean done = new AtomicBoolean();
|
AtomicBoolean done = new AtomicBoolean();
|
||||||
Single<Integer> result = hubConnection.invoke(Integer.class, "echo", "message");
|
Single<Integer> result = hubConnection.invoke(Integer.class, "echo", "message");
|
||||||
|
|
@ -403,7 +392,6 @@ class HubConnectionTest {
|
||||||
HubConnection hubConnection = TestUtils.createHubConnection("http://example.com", mockTransport);
|
HubConnection hubConnection = TestUtils.createHubConnection("http://example.com", mockTransport);
|
||||||
|
|
||||||
hubConnection.start();
|
hubConnection.start();
|
||||||
mockTransport.receiveMessage("{}" + RECORD_SEPARATOR);
|
|
||||||
|
|
||||||
AtomicBoolean doneFirst = new AtomicBoolean();
|
AtomicBoolean doneFirst = new AtomicBoolean();
|
||||||
AtomicBoolean doneSecond = new AtomicBoolean();
|
AtomicBoolean doneSecond = new AtomicBoolean();
|
||||||
|
|
@ -430,7 +418,6 @@ class HubConnectionTest {
|
||||||
HubConnection hubConnection = TestUtils.createHubConnection("http://example.com", mockTransport);
|
HubConnection hubConnection = TestUtils.createHubConnection("http://example.com", mockTransport);
|
||||||
|
|
||||||
hubConnection.start();
|
hubConnection.start();
|
||||||
mockTransport.receiveMessage("{}" + RECORD_SEPARATOR);
|
|
||||||
|
|
||||||
AtomicBoolean done = new AtomicBoolean();
|
AtomicBoolean done = new AtomicBoolean();
|
||||||
// int.class is a primitive type and since we use Class.cast to cast an Object to the expected return type
|
// int.class is a primitive type and since we use Class.cast to cast an Object to the expected return type
|
||||||
|
|
@ -450,7 +437,6 @@ class HubConnectionTest {
|
||||||
HubConnection hubConnection = TestUtils.createHubConnection("http://example.com", mockTransport);
|
HubConnection hubConnection = TestUtils.createHubConnection("http://example.com", mockTransport);
|
||||||
|
|
||||||
hubConnection.start();
|
hubConnection.start();
|
||||||
mockTransport.receiveMessage("{}" + RECORD_SEPARATOR);
|
|
||||||
|
|
||||||
AtomicBoolean done = new AtomicBoolean();
|
AtomicBoolean done = new AtomicBoolean();
|
||||||
Single<Integer> result = hubConnection.invoke(int.class, "echo", "message");
|
Single<Integer> result = hubConnection.invoke(int.class, "echo", "message");
|
||||||
|
|
@ -476,7 +462,6 @@ class HubConnectionTest {
|
||||||
HubConnection hubConnection = TestUtils.createHubConnection("http://example.com", mockTransport);
|
HubConnection hubConnection = TestUtils.createHubConnection("http://example.com", mockTransport);
|
||||||
|
|
||||||
hubConnection.start();
|
hubConnection.start();
|
||||||
mockTransport.receiveMessage("{}" + RECORD_SEPARATOR);
|
|
||||||
|
|
||||||
AtomicBoolean done = new AtomicBoolean();
|
AtomicBoolean done = new AtomicBoolean();
|
||||||
Single<Integer> result = hubConnection.invoke(int.class, "echo", "message");
|
Single<Integer> result = hubConnection.invoke(int.class, "echo", "message");
|
||||||
|
|
@ -508,7 +493,6 @@ class HubConnectionTest {
|
||||||
});
|
});
|
||||||
|
|
||||||
hubConnection.start();
|
hubConnection.start();
|
||||||
mockTransport.receiveMessage("{}" + RECORD_SEPARATOR);
|
|
||||||
mockTransport.receiveMessage("{\"type\":1,\"target\":\"inc\",\"arguments\":[]}" + RECORD_SEPARATOR);
|
mockTransport.receiveMessage("{\"type\":1,\"target\":\"inc\",\"arguments\":[]}" + RECORD_SEPARATOR);
|
||||||
|
|
||||||
// Confirming that our handler was called and that the counter property was incremented.
|
// Confirming that our handler was called and that the counter property was incremented.
|
||||||
|
|
@ -527,7 +511,6 @@ class HubConnectionTest {
|
||||||
}, String.class);
|
}, String.class);
|
||||||
|
|
||||||
hubConnection.start();
|
hubConnection.start();
|
||||||
mockTransport.receiveMessage("{}" + RECORD_SEPARATOR);
|
|
||||||
mockTransport.receiveMessage("{\"type\":1,\"target\":\"inc\",\"arguments\":[\"Hello World\"]}" + RECORD_SEPARATOR);
|
mockTransport.receiveMessage("{\"type\":1,\"target\":\"inc\",\"arguments\":[\"Hello World\"]}" + RECORD_SEPARATOR);
|
||||||
hubConnection.send("inc", "Hello World");
|
hubConnection.send("inc", "Hello World");
|
||||||
|
|
||||||
|
|
@ -552,7 +535,6 @@ class HubConnectionTest {
|
||||||
}, String.class, Double.class);
|
}, String.class, Double.class);
|
||||||
|
|
||||||
hubConnection.start();
|
hubConnection.start();
|
||||||
mockTransport.receiveMessage("{}" + RECORD_SEPARATOR);
|
|
||||||
mockTransport.receiveMessage("{\"type\":1,\"target\":\"inc\",\"arguments\":[\"Hello World\", 12]}" + RECORD_SEPARATOR);
|
mockTransport.receiveMessage("{\"type\":1,\"target\":\"inc\",\"arguments\":[\"Hello World\", 12]}" + RECORD_SEPARATOR);
|
||||||
hubConnection.send("inc", "Hello World", 12);
|
hubConnection.send("inc", "Hello World", 12);
|
||||||
|
|
||||||
|
|
@ -581,7 +563,6 @@ class HubConnectionTest {
|
||||||
}, String.class, String.class, String.class);
|
}, String.class, String.class, String.class);
|
||||||
|
|
||||||
hubConnection.start();
|
hubConnection.start();
|
||||||
mockTransport.receiveMessage("{}" + RECORD_SEPARATOR);
|
|
||||||
mockTransport.receiveMessage("{\"type\":1,\"target\":\"inc\",\"arguments\":[\"A\", \"B\", \"C\"]}" + RECORD_SEPARATOR);
|
mockTransport.receiveMessage("{\"type\":1,\"target\":\"inc\",\"arguments\":[\"A\", \"B\", \"C\"]}" + RECORD_SEPARATOR);
|
||||||
hubConnection.send("inc", "A", "B", "C");
|
hubConnection.send("inc", "A", "B", "C");
|
||||||
|
|
||||||
|
|
@ -614,7 +595,6 @@ class HubConnectionTest {
|
||||||
}, String.class, String.class, String.class, String.class);
|
}, String.class, String.class, String.class, String.class);
|
||||||
|
|
||||||
hubConnection.start();
|
hubConnection.start();
|
||||||
mockTransport.receiveMessage("{}" + RECORD_SEPARATOR);
|
|
||||||
mockTransport.receiveMessage("{\"type\":1,\"target\":\"inc\",\"arguments\":[\"A\", \"B\", \"C\", \"D\"]}" + RECORD_SEPARATOR);
|
mockTransport.receiveMessage("{\"type\":1,\"target\":\"inc\",\"arguments\":[\"A\", \"B\", \"C\", \"D\"]}" + RECORD_SEPARATOR);
|
||||||
|
|
||||||
// Confirming that our handler was called and the correct message was passed in.
|
// Confirming that our handler was called and the correct message was passed in.
|
||||||
|
|
@ -650,7 +630,6 @@ class HubConnectionTest {
|
||||||
}, String.class, String.class, String.class, Boolean.class, Double.class);
|
}, String.class, String.class, String.class, Boolean.class, Double.class);
|
||||||
|
|
||||||
hubConnection.start();
|
hubConnection.start();
|
||||||
mockTransport.receiveMessage("{}" + RECORD_SEPARATOR);
|
|
||||||
mockTransport.receiveMessage("{\"type\":1,\"target\":\"inc\",\"arguments\":[\"A\", \"B\", \"C\",true,12 ]}" + RECORD_SEPARATOR);
|
mockTransport.receiveMessage("{\"type\":1,\"target\":\"inc\",\"arguments\":[\"A\", \"B\", \"C\",true,12 ]}" + RECORD_SEPARATOR);
|
||||||
|
|
||||||
// Confirming that our handler was called and the correct message was passed in.
|
// Confirming that our handler was called and the correct message was passed in.
|
||||||
|
|
@ -690,7 +669,6 @@ class HubConnectionTest {
|
||||||
}, String.class, String.class, String.class, Boolean.class, Double.class, String.class);
|
}, String.class, String.class, String.class, Boolean.class, Double.class, String.class);
|
||||||
|
|
||||||
hubConnection.start();
|
hubConnection.start();
|
||||||
mockTransport.receiveMessage("{}" + RECORD_SEPARATOR);
|
|
||||||
mockTransport.receiveMessage("{\"type\":1,\"target\":\"inc\",\"arguments\":[\"A\", \"B\", \"C\",true,12,\"D\"]}" + RECORD_SEPARATOR);
|
mockTransport.receiveMessage("{\"type\":1,\"target\":\"inc\",\"arguments\":[\"A\", \"B\", \"C\",true,12,\"D\"]}" + RECORD_SEPARATOR);
|
||||||
|
|
||||||
// Confirming that our handler was called and the correct message was passed in.
|
// Confirming that our handler was called and the correct message was passed in.
|
||||||
|
|
@ -734,7 +712,6 @@ class HubConnectionTest {
|
||||||
}, String.class, String.class, String.class, Boolean.class, Double.class, String.class, String.class);
|
}, String.class, String.class, String.class, Boolean.class, Double.class, String.class, String.class);
|
||||||
|
|
||||||
hubConnection.start();
|
hubConnection.start();
|
||||||
mockTransport.receiveMessage("{}" + RECORD_SEPARATOR);
|
|
||||||
mockTransport.receiveMessage("{\"type\":1,\"target\":\"inc\",\"arguments\":[\"A\", \"B\", \"C\",true,12,\"D\",\"E\"]}" + RECORD_SEPARATOR);
|
mockTransport.receiveMessage("{\"type\":1,\"target\":\"inc\",\"arguments\":[\"A\", \"B\", \"C\",true,12,\"D\",\"E\"]}" + RECORD_SEPARATOR);
|
||||||
|
|
||||||
// Confirming that our handler was called and the correct message was passed in.
|
// Confirming that our handler was called and the correct message was passed in.
|
||||||
|
|
@ -782,7 +759,6 @@ class HubConnectionTest {
|
||||||
}, String.class, String.class, String.class, Boolean.class, Double.class, String.class, String.class, String.class);
|
}, String.class, String.class, String.class, Boolean.class, Double.class, String.class, String.class, String.class);
|
||||||
|
|
||||||
hubConnection.start();
|
hubConnection.start();
|
||||||
mockTransport.receiveMessage("{}" + RECORD_SEPARATOR);
|
|
||||||
mockTransport.receiveMessage("{\"type\":1,\"target\":\"inc\",\"arguments\":[\"A\", \"B\", \"C\",true,12,\"D\",\"E\",\"F\"]}" + RECORD_SEPARATOR);
|
mockTransport.receiveMessage("{\"type\":1,\"target\":\"inc\",\"arguments\":[\"A\", \"B\", \"C\",true,12,\"D\",\"E\",\"F\"]}" + RECORD_SEPARATOR);
|
||||||
// Confirming that our handler was called and the correct message was passed in.
|
// Confirming that our handler was called and the correct message was passed in.
|
||||||
assertEquals("A", value1.get());
|
assertEquals("A", value1.get());
|
||||||
|
|
@ -815,7 +791,6 @@ class HubConnectionTest {
|
||||||
}, Custom.class);
|
}, Custom.class);
|
||||||
|
|
||||||
hubConnection.start();
|
hubConnection.start();
|
||||||
mockTransport.receiveMessage("{}" + RECORD_SEPARATOR);
|
|
||||||
mockTransport.receiveMessage("{\"type\":1,\"target\":\"inc\",\"arguments\":[{\"number\":1,\"str\":\"A\",\"bools\":[true,false]}]}" + RECORD_SEPARATOR);
|
mockTransport.receiveMessage("{\"type\":1,\"target\":\"inc\",\"arguments\":[{\"number\":1,\"str\":\"A\",\"bools\":[true,false]}]}" + RECORD_SEPARATOR);
|
||||||
|
|
||||||
// Confirming that our handler was called and the correct message was passed in.
|
// Confirming that our handler was called and the correct message was passed in.
|
||||||
|
|
@ -830,7 +805,7 @@ class HubConnectionTest {
|
||||||
@Test
|
@Test
|
||||||
public void receiveHandshakeResponseAndMessage() throws Exception {
|
public void receiveHandshakeResponseAndMessage() throws Exception {
|
||||||
AtomicReference<Double> value = new AtomicReference<Double>(0.0);
|
AtomicReference<Double> value = new AtomicReference<Double>(0.0);
|
||||||
MockTransport mockTransport = new MockTransport();
|
MockTransport mockTransport = new MockTransport(false);
|
||||||
HubConnection hubConnection = TestUtils.createHubConnection("http://example.com", mockTransport);
|
HubConnection hubConnection = TestUtils.createHubConnection("http://example.com", mockTransport);
|
||||||
|
|
||||||
hubConnection.on("inc", () ->{
|
hubConnection.on("inc", () ->{
|
||||||
|
|
@ -898,7 +873,6 @@ class HubConnectionTest {
|
||||||
assertEquals(ex.getMessage(), "There was an error");
|
assertEquals(ex.getMessage(), "There was an error");
|
||||||
});
|
});
|
||||||
hubConnection.start();
|
hubConnection.start();
|
||||||
mockTransport.receiveMessage("{}" + RECORD_SEPARATOR);
|
|
||||||
|
|
||||||
assertEquals(HubConnectionState.CONNECTED, hubConnection.getConnectionState());
|
assertEquals(HubConnectionState.CONNECTED, hubConnection.getConnectionState());
|
||||||
|
|
||||||
|
|
@ -938,7 +912,6 @@ class HubConnectionTest {
|
||||||
}, String.class);
|
}, String.class);
|
||||||
|
|
||||||
Completable startFuture = hubConnection.start();
|
Completable startFuture = hubConnection.start();
|
||||||
mockTransport.receiveMessage("{}" + RECORD_SEPARATOR);
|
|
||||||
|
|
||||||
startFuture.blockingAwait(1000, TimeUnit.MILLISECONDS);
|
startFuture.blockingAwait(1000, TimeUnit.MILLISECONDS);
|
||||||
RuntimeException exception = assertThrows(RuntimeException.class, () -> mockTransport.receiveMessage("{\"type\":1,\"target\":\"Send\",\"arguments\":[]}" + RECORD_SEPARATOR));
|
RuntimeException exception = assertThrows(RuntimeException.class, () -> mockTransport.receiveMessage("{\"type\":1,\"target\":\"Send\",\"arguments\":[]}" + RECORD_SEPARATOR));
|
||||||
|
|
@ -1091,9 +1064,43 @@ class HubConnectionTest {
|
||||||
assertEquals("Bearer newToken", token.get());
|
assertEquals("Bearer newToken", token.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void connectionTimesOutIfServerDoesNotSendMessage() throws Exception {
|
||||||
|
HubConnection hubConnection = TestUtils.createHubConnection("http://example.com");
|
||||||
|
hubConnection.setServerTimeout(Duration.ofMillis(1));
|
||||||
|
hubConnection.setTickRate(Duration.ofMillis(1));
|
||||||
|
CompletableFuture<Exception> closedFuture = new CompletableFuture<>();
|
||||||
|
hubConnection.onClosed((e) -> {
|
||||||
|
closedFuture.complete(e);
|
||||||
|
});
|
||||||
|
|
||||||
|
hubConnection.start().blockingAwait(1000, TimeUnit.MILLISECONDS);
|
||||||
|
|
||||||
|
assertEquals("Server timeout elapsed without receiving a message from the server.", closedFuture.get(1000, TimeUnit.MILLISECONDS).getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void connectionSendsPingsRegularly() throws InterruptedException, ExecutionException, TimeoutException, Exception {
|
||||||
|
MockTransport mockTransport = new MockTransport(true, false);
|
||||||
|
HubConnection hubConnection = TestUtils.createHubConnection("http://example.com", mockTransport);
|
||||||
|
hubConnection.setKeepAliveInterval(Duration.ofMillis(1));
|
||||||
|
hubConnection.setTickRate(Duration.ofMillis(1));
|
||||||
|
|
||||||
|
hubConnection.start().blockingAwait(1000, TimeUnit.MILLISECONDS);
|
||||||
|
|
||||||
|
TimeUnit.MILLISECONDS.sleep(100);
|
||||||
|
hubConnection.stop();
|
||||||
|
|
||||||
|
String[] sentMessages = mockTransport.getSentMessages();
|
||||||
|
assertTrue(sentMessages.length > 1);
|
||||||
|
for (int i = 1; i < sentMessages.length; i++) {
|
||||||
|
assertEquals("{\"type\":6}" + RECORD_SEPARATOR, sentMessages[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void hubConnectionCanBeStartedAfterBeingStopped() throws Exception {
|
public void hubConnectionCanBeStartedAfterBeingStopped() throws Exception {
|
||||||
MockTransport transport = new MockTransport(true);
|
MockTransport transport = new MockTransport();
|
||||||
HubConnection hubConnection = HubConnectionBuilder
|
HubConnection hubConnection = HubConnectionBuilder
|
||||||
.create("http://example.com")
|
.create("http://example.com")
|
||||||
.withTransport(transport)
|
.withTransport(transport)
|
||||||
|
|
@ -1112,7 +1119,7 @@ class HubConnectionTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void hubConnectionCanBeStartedAfterBeingStoppedAndRedirected() throws Exception {
|
public void hubConnectionCanBeStartedAfterBeingStoppedAndRedirected() throws Exception {
|
||||||
MockTransport mockTransport = new MockTransport(true);
|
MockTransport mockTransport = new MockTransport();
|
||||||
TestHttpClient client = new TestHttpClient()
|
TestHttpClient client = new TestHttpClient()
|
||||||
.on("POST", "http://example.com/negotiate", (req) -> CompletableFuture
|
.on("POST", "http://example.com/negotiate", (req) -> CompletableFuture
|
||||||
.completedFuture(new HttpResponse(200, "", "{\"url\":\"http://testexample.com/\"}")))
|
.completedFuture(new HttpResponse(200, "", "{\"url\":\"http://testexample.com/\"}")))
|
||||||
|
|
|
||||||
|
|
@ -12,15 +12,22 @@ class MockTransport implements Transport {
|
||||||
private ArrayList<String> sentMessages = new ArrayList<>();
|
private ArrayList<String> sentMessages = new ArrayList<>();
|
||||||
private String url;
|
private String url;
|
||||||
private Consumer<String> onClose;
|
private Consumer<String> onClose;
|
||||||
private boolean autoHandshake;
|
final private boolean ignorePings;
|
||||||
|
final private boolean autoHandshake;
|
||||||
|
|
||||||
private static final String RECORD_SEPARATOR = "\u001e";
|
private static final String RECORD_SEPARATOR = "\u001e";
|
||||||
|
|
||||||
public MockTransport() {
|
public MockTransport() {
|
||||||
|
this(true, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public MockTransport(boolean autoHandshake) {
|
public MockTransport(boolean autoHandshake) {
|
||||||
|
this(autoHandshake, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public MockTransport(boolean autoHandshake, boolean ignorePings) {
|
||||||
this.autoHandshake = autoHandshake;
|
this.autoHandshake = autoHandshake;
|
||||||
|
this.ignorePings = ignorePings;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -38,7 +45,9 @@ class MockTransport implements Transport {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CompletableFuture send(String message) {
|
public CompletableFuture send(String message) {
|
||||||
sentMessages.add(message);
|
if (!(ignorePings && message.equals("{\"type\":6}" + RECORD_SEPARATOR))) {
|
||||||
|
sentMessages.add(message);
|
||||||
|
}
|
||||||
return CompletableFuture.completedFuture(null);
|
return CompletableFuture.completedFuture(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue