diff --git a/SignalR.sln b/SignalR.sln
index f53f22d4ca..7f925af865 100644
--- a/SignalR.sln
+++ b/SignalR.sln
@@ -89,7 +89,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Crankier", "benchmarkapps\C
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "benchmarkapps", "benchmarkapps", "{43F352F3-4E2B-4ED7-901B-36E6671251F5}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.SignalR.Specification.Tests", "src\Microsoft.AspNetCore.SignalR.Specification.Tests\Microsoft.AspNetCore.SignalR.Specification.Tests.csproj", "{2B03333F-3ACD-474C-862B-FA97D3BA03B5}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.SignalR.Specification.Tests", "src\Microsoft.AspNetCore.SignalR.Specification.Tests\Microsoft.AspNetCore.SignalR.Specification.Tests.csproj", "{2B03333F-3ACD-474C-862B-FA97D3BA03B5}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.SignalR.StackExchangeRedis", "src\Microsoft.AspNetCore.SignalR.StackExchangeRedis\Microsoft.AspNetCore.SignalR.StackExchangeRedis.csproj", "{D1334F29-5C19-4C7B-B62D-0A2F23AFB31C}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.SignalR.StackExchangeRedis.Tests", "test\Microsoft.AspNetCore.SignalR.StackExchangeRedis.Tests\Microsoft.AspNetCore.SignalR.StackExchangeRedis.Tests.csproj", "{A5006087-81B0-4C62-B847-50ED5C37069D}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -213,6 +217,14 @@ Global
{2B03333F-3ACD-474C-862B-FA97D3BA03B5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2B03333F-3ACD-474C-862B-FA97D3BA03B5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2B03333F-3ACD-474C-862B-FA97D3BA03B5}.Release|Any CPU.Build.0 = Release|Any CPU
+ {D1334F29-5C19-4C7B-B62D-0A2F23AFB31C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {D1334F29-5C19-4C7B-B62D-0A2F23AFB31C}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {D1334F29-5C19-4C7B-B62D-0A2F23AFB31C}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {D1334F29-5C19-4C7B-B62D-0A2F23AFB31C}.Release|Any CPU.Build.0 = Release|Any CPU
+ {A5006087-81B0-4C62-B847-50ED5C37069D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {A5006087-81B0-4C62-B847-50ED5C37069D}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {A5006087-81B0-4C62-B847-50ED5C37069D}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {A5006087-81B0-4C62-B847-50ED5C37069D}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -247,6 +259,8 @@ Global
{8C75AC94-C980-4FE1-9F79-6CED3C8665CE} = {43F352F3-4E2B-4ED7-901B-36E6671251F5}
{8D3E3E7D-452B-44F4-86CA-111003EA11ED} = {43F352F3-4E2B-4ED7-901B-36E6671251F5}
{2B03333F-3ACD-474C-862B-FA97D3BA03B5} = {DA69F624-5398-4884-87E4-B816698CDE65}
+ {D1334F29-5C19-4C7B-B62D-0A2F23AFB31C} = {DA69F624-5398-4884-87E4-B816698CDE65}
+ {A5006087-81B0-4C62-B847-50ED5C37069D} = {6A35B453-52EC-48AF-89CA-D4A69800F131}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {7945A4E4-ACDB-4F6E-95CA-6AC6E7C2CD59}
diff --git a/benchmarks/Microsoft.AspNetCore.SignalR.Microbenchmarks/Microsoft.AspNetCore.SignalR.Microbenchmarks.csproj b/benchmarks/Microsoft.AspNetCore.SignalR.Microbenchmarks/Microsoft.AspNetCore.SignalR.Microbenchmarks.csproj
index d391a18436..0143f5ffae 100644
--- a/benchmarks/Microsoft.AspNetCore.SignalR.Microbenchmarks/Microsoft.AspNetCore.SignalR.Microbenchmarks.csproj
+++ b/benchmarks/Microsoft.AspNetCore.SignalR.Microbenchmarks/Microsoft.AspNetCore.SignalR.Microbenchmarks.csproj
@@ -17,8 +17,9 @@
-
+
+
diff --git a/benchmarks/Microsoft.AspNetCore.SignalR.Microbenchmarks/RedisHubLifetimeManagerBenchmark.cs b/benchmarks/Microsoft.AspNetCore.SignalR.Microbenchmarks/RedisHubLifetimeManagerBenchmark.cs
index b0eea531bb..606c53673c 100644
--- a/benchmarks/Microsoft.AspNetCore.SignalR.Microbenchmarks/RedisHubLifetimeManagerBenchmark.cs
+++ b/benchmarks/Microsoft.AspNetCore.SignalR.Microbenchmarks/RedisHubLifetimeManagerBenchmark.cs
@@ -10,7 +10,7 @@ using BenchmarkDotNet.Attributes;
using Microsoft.AspNetCore.Connections;
using Microsoft.AspNetCore.SignalR.Internal;
using Microsoft.AspNetCore.SignalR.Protocol;
-using Microsoft.AspNetCore.SignalR.Redis;
+using Microsoft.AspNetCore.SignalR.StackExchangeRedis;
using Microsoft.AspNetCore.SignalR.Tests;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
@@ -34,7 +34,8 @@ namespace Microsoft.AspNetCore.SignalR.Microbenchmarks
[Params(2, 20)]
public int ProtocolCount { get; set; }
- [GlobalSetup]
+ // Re-enable micro-benchmark when https://github.com/aspnet/SignalR/issues/3088 is fixed
+ // [GlobalSetup]
public void GlobalSetup()
{
var server = new TestRedisServer();
@@ -90,7 +91,7 @@ namespace Microsoft.AspNetCore.SignalR.Microbenchmarks
_users.Add("EvenUser");
_users.Add("OddUser");
- _args = new object[] {"Foo"};
+ _args = new object[] { "Foo" };
}
private IEnumerable GenerateProtocols(int protocolCount)
@@ -111,55 +112,55 @@ namespace Microsoft.AspNetCore.SignalR.Microbenchmarks
}
}
- [Benchmark]
+ //[Benchmark]
public async Task SendAll()
{
await _manager1.SendAllAsync("Test", _args);
}
- [Benchmark]
+ //[Benchmark]
public async Task SendGroup()
{
await _manager1.SendGroupAsync("Everyone", "Test", _args);
}
- [Benchmark]
+ //[Benchmark]
public async Task SendUser()
{
await _manager1.SendUserAsync("EvenUser", "Test", _args);
}
- [Benchmark]
+ //[Benchmark]
public async Task SendConnection()
{
await _manager1.SendConnectionAsync(_clients[0].Connection.ConnectionId, "Test", _args);
}
- [Benchmark]
+ //[Benchmark]
public async Task SendConnections()
{
await _manager1.SendConnectionsAsync(_sendIds, "Test", _args);
}
- [Benchmark]
+ //[Benchmark]
public async Task SendAllExcept()
{
await _manager1.SendAllExceptAsync("Test", _args, _excludedConnectionIds);
}
- [Benchmark]
+ //[Benchmark]
public async Task SendGroupExcept()
{
await _manager1.SendGroupExceptAsync("Everyone", "Test", _args, _excludedConnectionIds);
}
- [Benchmark]
+ //[Benchmark]
public async Task SendGroups()
{
await _manager1.SendGroupsAsync(_groups, "Test", _args);
}
- [Benchmark]
+ //[Benchmark]
public async Task SendUsers()
{
await _manager1.SendUsersAsync(_users, "Test", _args);
diff --git a/benchmarks/Microsoft.AspNetCore.SignalR.Microbenchmarks/RedisProtocolBenchmark.cs b/benchmarks/Microsoft.AspNetCore.SignalR.Microbenchmarks/RedisProtocolBenchmark.cs
index 1f72f922fe..c87d0e5226 100644
--- a/benchmarks/Microsoft.AspNetCore.SignalR.Microbenchmarks/RedisProtocolBenchmark.cs
+++ b/benchmarks/Microsoft.AspNetCore.SignalR.Microbenchmarks/RedisProtocolBenchmark.cs
@@ -7,7 +7,7 @@ using System.Collections.Generic;
using BenchmarkDotNet.Attributes;
using Microsoft.AspNetCore.Connections;
using Microsoft.AspNetCore.SignalR.Protocol;
-using Microsoft.AspNetCore.SignalR.Redis.Internal;
+using Microsoft.AspNetCore.SignalR.StackExchangeRedis.Internal;
namespace Microsoft.AspNetCore.SignalR.Microbenchmarks
{
diff --git a/build/dependencies.props b/build/dependencies.props
index 3bb618737f..743b23172b 100644
--- a/build/dependencies.props
+++ b/build/dependencies.props
@@ -62,6 +62,7 @@
2.0.3
11.0.2
1.2.6
+ 2.0.513
4.6.0-preview1-26907-04
4.6.0-preview1-26907-04
4.6.0-preview1-26717-04
diff --git a/clients/java/signalr/src/main/java/com/microsoft/signalr/CallbackMap.java b/clients/java/signalr/src/main/java/com/microsoft/signalr/CallbackMap.java
index e5f7b58edd..134ab2ed09 100644
--- a/clients/java/signalr/src/main/java/com/microsoft/signalr/CallbackMap.java
+++ b/clients/java/signalr/src/main/java/com/microsoft/signalr/CallbackMap.java
@@ -7,7 +7,7 @@ import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
class CallbackMap {
- private Map> handlers = new ConcurrentHashMap<>();
+ private final Map> handlers = new ConcurrentHashMap<>();
public InvocationHandler put(String target, ActionBase action, Class>... classes) {
InvocationHandler handler = new InvocationHandler(action, classes);
diff --git a/clients/java/signalr/src/main/java/com/microsoft/signalr/DefaultHttpClient.java b/clients/java/signalr/src/main/java/com/microsoft/signalr/DefaultHttpClient.java
index 2f42e1f5f7..a2e81c8e70 100644
--- a/clients/java/signalr/src/main/java/com/microsoft/signalr/DefaultHttpClient.java
+++ b/clients/java/signalr/src/main/java/com/microsoft/signalr/DefaultHttpClient.java
@@ -22,8 +22,8 @@ import okhttp3.RequestBody;
import okhttp3.Response;
import okhttp3.ResponseBody;
-class DefaultHttpClient extends HttpClient {
- private OkHttpClient client;
+final class DefaultHttpClient extends HttpClient {
+ private final OkHttpClient client;
private Logger logger;
public DefaultHttpClient(Logger logger) {
diff --git a/clients/java/signalr/src/main/java/com/microsoft/signalr/HandshakeProtocol.java b/clients/java/signalr/src/main/java/com/microsoft/signalr/HandshakeProtocol.java
index ca7987c68c..4c1d2ad896 100644
--- a/clients/java/signalr/src/main/java/com/microsoft/signalr/HandshakeProtocol.java
+++ b/clients/java/signalr/src/main/java/com/microsoft/signalr/HandshakeProtocol.java
@@ -5,7 +5,7 @@ package com.microsoft.signalr;
import com.google.gson.Gson;
-class HandshakeProtocol {
+final class HandshakeProtocol {
private static final Gson gson = new Gson();
private static final String RECORD_SEPARATOR = "\u001e";
diff --git a/clients/java/signalr/src/main/java/com/microsoft/signalr/HttpHubConnectionBuilder.java b/clients/java/signalr/src/main/java/com/microsoft/signalr/HttpHubConnectionBuilder.java
index c2594826a8..9d059ab8ba 100644
--- a/clients/java/signalr/src/main/java/com/microsoft/signalr/HttpHubConnectionBuilder.java
+++ b/clients/java/signalr/src/main/java/com/microsoft/signalr/HttpHubConnectionBuilder.java
@@ -3,18 +3,18 @@
package com.microsoft.signalr;
-import java.util.concurrent.CompletableFuture;
-import java.util.function.Supplier;
+import java.time.Duration;
import io.reactivex.Single;
public class HttpHubConnectionBuilder {
- private String url;
+ private final String url;
private Transport transport;
private Logger logger;
private HttpClient httpClient;
private boolean skipNegotiate;
private Single accessTokenProvider;
+ private Duration handshakeResponseTimeout;
HttpHubConnectionBuilder(String url) {
this.url = url;
@@ -56,7 +56,12 @@ public class HttpHubConnectionBuilder {
return this;
}
+ public HttpHubConnectionBuilder withHandshakeResponseTimeout(Duration timeout) {
+ this.handshakeResponseTimeout = timeout;
+ return this;
+ }
+
public HubConnection build() {
- return new HubConnection(url, transport, skipNegotiate, logger, httpClient, accessTokenProvider);
+ return new HubConnection(url, transport, skipNegotiate, logger, httpClient, accessTokenProvider, handshakeResponseTimeout);
}
}
\ No newline at end of file
diff --git a/clients/java/signalr/src/main/java/com/microsoft/signalr/HubConnection.java b/clients/java/signalr/src/main/java/com/microsoft/signalr/HubConnection.java
index 9d926023cc..df17a9421f 100644
--- a/clients/java/signalr/src/main/java/com/microsoft/signalr/HubConnection.java
+++ b/clients/java/signalr/src/main/java/com/microsoft/signalr/HubConnection.java
@@ -3,13 +3,17 @@
package com.microsoft.signalr;
-import java.io.IOException;
+import java.time.Duration;
import java.util.ArrayList;
+import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import java.util.concurrent.CompletableFuture;
+import java.util.Timer;
+import java.util.TimerTask;
+import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Consumer;
@@ -19,27 +23,56 @@ import io.reactivex.Single;
public class HubConnection {
private static final String RECORD_SEPARATOR = "\u001e";
- private static List> emptyArray = new ArrayList<>();
- private static int MAX_NEGOTIATE_ATTEMPTS = 100;
+ private static final List> emptyArray = new ArrayList<>();
+ private static final int MAX_NEGOTIATE_ATTEMPTS = 100;
- private String baseUrl;
+ private final String baseUrl;
private Transport transport;
private OnReceiveCallBack callback;
- private CallbackMap handlers = new CallbackMap();
+ private final CallbackMap handlers = new CallbackMap();
private HubProtocol protocol;
private Boolean handshakeReceived = false;
private HubConnectionState hubConnectionState = HubConnectionState.DISCONNECTED;
- private Lock hubConnectionStateLock = new ReentrantLock();
+ private final Lock hubConnectionStateLock = new ReentrantLock();
private Logger logger;
private List> onClosedCallbackList;
- private boolean skipNegotiate;
+ private final boolean skipNegotiate;
private Single accessTokenProvider;
- private Map headers = new HashMap<>();
+ private final Map headers = new HashMap<>();
private ConnectionState connectionState = null;
- private HttpClient httpClient;
+ private final HttpClient httpClient;
private String stopError;
+ private Timer pingTimer = null;
+ private final AtomicLong nextServerTimeout = new AtomicLong();
+ private final AtomicLong nextPingActivation = new AtomicLong();
+ private Duration keepAliveInterval = Duration.ofSeconds(15);
+ private Duration serverTimeout = Duration.ofSeconds(30);
+ private Duration tickRate = Duration.ofSeconds(1);
+ private CompletableFuture handshakeResponseFuture;
+ private Duration handshakeResponseTimeout = Duration.ofSeconds(15);
- HubConnection(String url, Transport transport, boolean skipNegotiate, Logger logger, HttpClient httpClient, Single accessTokenProvider) {
+ 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 accessTokenProvider, Duration handshakeResponseTimeout) {
if (url == null || url.isEmpty()) {
throw new IllegalArgumentException("A valid url is required.");
}
@@ -69,19 +102,34 @@ public class HubConnection {
this.transport = transport;
}
+ if (handshakeResponseTimeout != null) {
+ this.handshakeResponseTimeout = handshakeResponseTimeout;
+ }
+
this.skipNegotiate = skipNegotiate;
this.callback = (payload) -> {
+ resetServerTimeout();
if (!handshakeReceived) {
int handshakeLength = payload.indexOf(RECORD_SEPARATOR) + 1;
String handshakeResponseString = payload.substring(0, handshakeLength - 1);
- HandshakeResponseMessage handshakeResponse = HandshakeProtocol.parseHandshakeResponse(handshakeResponseString);
+ HandshakeResponseMessage handshakeResponse;
+ try {
+ handshakeResponse = HandshakeProtocol.parseHandshakeResponse(handshakeResponseString);
+ } catch (RuntimeException ex) {
+ RuntimeException exception = new RuntimeException("An invalid handshake response was received from the server.", ex);
+ handshakeResponseFuture.completeExceptionally(exception);
+ throw exception;
+ }
if (handshakeResponse.getHandshakeError() != null) {
String errorMessage = "Error in handshake " + handshakeResponse.getHandshakeError();
logger.log(LogLevel.Error, errorMessage);
- throw new RuntimeException(errorMessage);
+ RuntimeException exception = new RuntimeException(errorMessage);
+ handshakeResponseFuture.completeExceptionally(exception);
+ throw exception;
}
handshakeReceived = true;
+ handshakeResponseFuture.complete(null);
payload = payload.substring(handshakeLength);
// The payload only contained the handshake response so we can return.
@@ -134,6 +182,12 @@ public class HubConnection {
};
}
+ private void timeoutHandshakeResponse(long timeout, TimeUnit unit) {
+ ScheduledExecutorService scheduledThreadPool = Executors.newSingleThreadScheduledExecutor();
+ scheduledThreadPool.schedule(() -> handshakeResponseFuture.completeExceptionally(
+ new TimeoutException("Timed out waiting for the server to respond to the handshake message.")), timeout, unit);
+ }
+
private CompletableFuture handleNegotiate(String url) {
HttpRequest request = new HttpRequest();
request.addHeaders(this.headers);
@@ -142,12 +196,7 @@ public class HubConnection {
if (response.getStatusCode() != 200) {
throw new RuntimeException(String.format("Unexpected status code returned from negotiate: %d %s.", response.getStatusCode(), response.getStatusText()));
}
- NegotiateResponse negotiateResponse;
- try {
- negotiateResponse = new NegotiateResponse(response.getContent());
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
+ NegotiateResponse negotiateResponse = new NegotiateResponse(response.getContent());
if (negotiateResponse.getError() != null) {
throw new RuntimeException(negotiateResponse.getError());
@@ -184,8 +233,9 @@ public class HubConnection {
return Completable.complete();
}
+ handshakeResponseFuture = new CompletableFuture<>();
handshakeReceived = false;
- CompletableFuture tokenFuture = new CompletableFuture<>();
+ CompletableFuture tokenFuture = new CompletableFuture<>();
accessTokenProvider.subscribe(token -> {
if (token != null && !token.isEmpty()) {
this.headers.put("Authorization", "Bearer " + token);
@@ -213,15 +263,41 @@ public class HubConnection {
return transport.start(url).thenCompose((future) -> {
String handshake = HandshakeProtocol.createHandshakeRequestMessage(
new HandshakeRequestMessage(protocol.getName(), protocol.getVersion()));
- return transport.send(handshake).thenRun(() -> {
- hubConnectionStateLock.lock();
- try {
- hubConnectionState = HubConnectionState.CONNECTED;
- connectionState = new ConnectionState(this);
- logger.log(LogLevel.Information, "HubConnection started.");
- } finally {
- hubConnectionStateLock.unlock();
- }
+ return transport.send(handshake).thenCompose((innerFuture) -> {
+ timeoutHandshakeResponse(handshakeResponseTimeout.toMillis(), TimeUnit.MILLISECONDS);
+ return handshakeResponseFuture.thenRun(() -> {
+ hubConnectionStateLock.lock();
+ try {
+ hubConnectionState = HubConnectionState.CONNECTED;
+ connectionState = new ConnectionState(this);
+ 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 {
+ hubConnectionStateLock.unlock();
+ }
+ });
});
});
}));
@@ -308,6 +384,7 @@ public class HubConnection {
connectionState = null;
logger.log(LogLevel.Information, "HubConnection stopped.");
hubConnectionState = HubConnectionState.DISCONNECTED;
+ handshakeResponseFuture.complete(null);
} finally {
hubConnectionStateLock.unlock();
}
@@ -326,18 +403,18 @@ public class HubConnection {
*
* @param method The name of the server method to invoke.
* @param args The arguments to be passed to the method.
- * @throws Exception If there was an error while sending.
*/
- public void send(String method, Object... args) throws Exception {
+ public void send(String method, Object... args) {
if (hubConnectionState != HubConnectionState.CONNECTED) {
- throw new HubException("The 'send' method cannot be called if the connection is not active");
+ throw new RuntimeException("The 'send' method cannot be called if the connection is not active");
}
InvocationMessage invocationMessage = new InvocationMessage(null, method, args);
sendHubMessage(invocationMessage);
}
- public Single invoke(Class returnType, String method, Object... args) throws Exception {
+ @SuppressWarnings("unchecked")
+ public Single invoke(Class returnType, String method, Object... args) {
String id = connectionState.getNextInvocationId();
InvocationMessage invocationMessage = new InvocationMessage(id, method, args);
@@ -368,14 +445,24 @@ public class HubConnection {
return Single.fromFuture(future);
}
- private void sendHubMessage(HubMessage message) throws Exception {
+ private void sendHubMessage(HubMessage message) {
String serializedMessage = protocol.writeMessage(message);
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 {
- logger.log(LogLevel.Debug, "Sending %d message.", message.getMessageType().value);
+ logger.log(LogLevel.Debug, "Sending %s message.", message.getMessageType().name());
}
transport.send(serializedMessage);
+
+ resetKeepAlive();
+ }
+
+ private void resetServerTimeout() {
+ this.nextServerTimeout.set(System.currentTimeMillis() + serverTimeout.toMillis());
+ }
+
+ private void resetKeepAlive() {
+ this.nextPingActivation.set(System.currentTimeMillis() + keepAliveInterval.toMillis());
}
/**
@@ -684,7 +771,7 @@ public class HubConnection {
}
@Override
- public List> getParameterTypes(String methodName) throws Exception {
+ public List> getParameterTypes(String methodName) {
List handlers = connection.handlers.get(methodName);
if (handlers == null) {
logger.log(LogLevel.Warning, "Failed to find handler for '%s' method.", methodName);
@@ -692,7 +779,7 @@ public class HubConnection {
}
if (handlers.isEmpty()) {
- throw new Exception(String.format("There are no callbacks registered for the method '%s'.", methodName));
+ throw new RuntimeException(String.format("There are no callbacks registered for the method '%s'.", methodName));
}
return handlers.get(0).getClasses();
diff --git a/clients/java/signalr/src/main/java/com/microsoft/signalr/HubConnectionBuilder.java b/clients/java/signalr/src/main/java/com/microsoft/signalr/HubConnectionBuilder.java
index c5bc761071..3aee028ee4 100644
--- a/clients/java/signalr/src/main/java/com/microsoft/signalr/HubConnectionBuilder.java
+++ b/clients/java/signalr/src/main/java/com/microsoft/signalr/HubConnectionBuilder.java
@@ -4,12 +4,11 @@
package com.microsoft.signalr;
public abstract class HubConnectionBuilder {
-
public static HttpHubConnectionBuilder create(String url) {
if (url == null || url.isEmpty()) {
throw new IllegalArgumentException("A valid url is required.");
}
- return new HttpHubConnectionBuilder(url);
+ return new HttpHubConnectionBuilder(url);
}
public abstract HubConnection build();
diff --git a/clients/java/signalr/src/main/java/com/microsoft/signalr/HubException.java b/clients/java/signalr/src/main/java/com/microsoft/signalr/HubException.java
index 6c7872372f..a5f44433e4 100644
--- a/clients/java/signalr/src/main/java/com/microsoft/signalr/HubException.java
+++ b/clients/java/signalr/src/main/java/com/microsoft/signalr/HubException.java
@@ -3,7 +3,9 @@
package com.microsoft.signalr;
-public class HubException extends Exception {
+public class HubException extends RuntimeException {
+ private static final long serialVersionUID = -572019264269821519L;
+
public HubException() {
}
diff --git a/clients/java/signalr/src/main/java/com/microsoft/signalr/HubProtocol.java b/clients/java/signalr/src/main/java/com/microsoft/signalr/HubProtocol.java
index 6667f1e862..072fa6b506 100644
--- a/clients/java/signalr/src/main/java/com/microsoft/signalr/HubProtocol.java
+++ b/clients/java/signalr/src/main/java/com/microsoft/signalr/HubProtocol.java
@@ -16,7 +16,7 @@ interface HubProtocol {
* @param message A string representation of one or more {@link HubMessage}s.
* @return A list of {@link HubMessage}s.
*/
- HubMessage[] parseMessages(String message, InvocationBinder binder) throws Exception;
+ HubMessage[] parseMessages(String message, InvocationBinder binder);
/**
* Writes the specified {@link HubMessage} to a String.
diff --git a/clients/java/signalr/src/main/java/com/microsoft/signalr/InvocationBinder.java b/clients/java/signalr/src/main/java/com/microsoft/signalr/InvocationBinder.java
index 5ac9a9a6b8..3f3457f730 100644
--- a/clients/java/signalr/src/main/java/com/microsoft/signalr/InvocationBinder.java
+++ b/clients/java/signalr/src/main/java/com/microsoft/signalr/InvocationBinder.java
@@ -7,5 +7,5 @@ import java.util.List;
interface InvocationBinder {
Class> getReturnType(String invocationId);
- List> getParameterTypes(String methodName) throws Exception;
+ List> getParameterTypes(String methodName);
}
\ No newline at end of file
diff --git a/clients/java/signalr/src/main/java/com/microsoft/signalr/InvocationHandler.java b/clients/java/signalr/src/main/java/com/microsoft/signalr/InvocationHandler.java
index 4d10df0faf..64f8ed3a1d 100644
--- a/clients/java/signalr/src/main/java/com/microsoft/signalr/InvocationHandler.java
+++ b/clients/java/signalr/src/main/java/com/microsoft/signalr/InvocationHandler.java
@@ -7,8 +7,8 @@ import java.util.Arrays;
import java.util.List;
class InvocationHandler {
- private List> classes;
- private ActionBase action;
+ private final List> classes;
+ private final ActionBase action;
InvocationHandler(ActionBase action, Class>... classes) {
this.action = action;
diff --git a/clients/java/signalr/src/main/java/com/microsoft/signalr/InvocationRequest.java b/clients/java/signalr/src/main/java/com/microsoft/signalr/InvocationRequest.java
index fded3b9fa0..d4fa34c66e 100644
--- a/clients/java/signalr/src/main/java/com/microsoft/signalr/InvocationRequest.java
+++ b/clients/java/signalr/src/main/java/com/microsoft/signalr/InvocationRequest.java
@@ -6,9 +6,9 @@ package com.microsoft.signalr;
import java.util.concurrent.CompletableFuture;
class InvocationRequest {
- private Class> returnType;
- private CompletableFuture