diff --git a/src/Microsoft.AspNet.SignalR.Client/Connection.cs b/src/Microsoft.AspNet.SignalR.Client/Connection.cs index 8340b4f322..cdd81328fd 100644 --- a/src/Microsoft.AspNet.SignalR.Client/Connection.cs +++ b/src/Microsoft.AspNet.SignalR.Client/Connection.cs @@ -32,7 +32,8 @@ public class Connection : IConnection, IDisposable internal static readonly TimeSpan DefaultAbortTimeout = TimeSpan.FromSeconds(30); private static readonly Version MinimumSupportedVersion = new Version(1, 4); - private static readonly Version MaximumSupportedVersion = new Version(2, 0); + private static readonly Version MaximumSupportedVersion = new Version(2, 1); + private static readonly Version MinimumSupportedNegotiateRedirectVersion = new Version(2, 0); private static readonly int MaxRedirects = 100; private static Version _assemblyVersion; @@ -205,7 +206,7 @@ public Connection(string url, string queryString) DeadlockErrorTimeout = TimeSpan.FromSeconds(10); // Current client protocol - Protocol = new Version(2, 0); + Protocol = new Version(2, 1); } /// @@ -529,9 +530,9 @@ Task StartNegotiation() return transport.Negotiate(this, _connectionData) .Then(negotiationResponse => { - VerifyProtocolVersion(negotiationResponse.ProtocolVersion); + var protocolVersion = VerifyProtocolVersion(negotiationResponse.ProtocolVersion); - if (negotiationResponse.ProtocolVersion.Equals("2.0")) + if (protocolVersion >= MinimumSupportedNegotiateRedirectVersion) { if (!string.IsNullOrEmpty(negotiationResponse.Error)) { @@ -636,7 +637,7 @@ bool IConnection.ChangeState(ConnectionState oldState, ConnectionState newState) return false; } - private void VerifyProtocolVersion(string versionString) + private Version VerifyProtocolVersion(string versionString) { Version version; @@ -649,6 +650,8 @@ private void VerifyProtocolVersion(string versionString) Protocol, versionString ?? "null")); } + + return version; } /// diff --git a/src/Microsoft.AspNet.SignalR.JS/jquery.signalR.core.js b/src/Microsoft.AspNet.SignalR.JS/jquery.signalR.core.js index 9d437173d4..1a23bb6152 100644 --- a/src/Microsoft.AspNet.SignalR.JS/jquery.signalR.core.js +++ b/src/Microsoft.AspNet.SignalR.JS/jquery.signalR.core.js @@ -413,12 +413,16 @@ state: signalR.connectionState.disconnected, - clientProtocol: "2.0", + clientProtocol: "2.1", // We want to support older servers since the 2.0 change is to support redirection results, which isn't // really breaking in the protocol. So if a user updates their client to 2.0 protocol version there's - // no reason they can't still connect to a 1.5 server. - supportedProtocols: ["1.5", "2.0"], + // no reason they can't still connect to a 1.5 server. The 2.1 protocol is sent by the client so the SignalR + // service knows the client will use they query string returned via the RedirectUrl for subsequent requests. + // It doesn't matter whether the server reflects back 2.1 or continues using 2.0 as the protocol version. + supportedProtocols: ["1.5", "2.0", "2.1"], + + negotiateRedirectSupportedProtocols: ["2.0", "2.1"], reconnectDelay: 2000, @@ -739,8 +743,10 @@ return; } - // Check for a redirect response (which must have a ProtocolVersion of 2.0) - if (res.ProtocolVersion === "2.0") { + // Check for a redirect response (which must have a ProtocolVersion of 2.0 or greater) + // ProtocolVersion 2.1 is the highest supported by the client, so we can just check for 2.0 or 2.1 for now + // instead of trying to do proper version string comparison in JavaScript. + if (connection.negotiateRedirectSupportedProtocols.indexOf(res.ProtocolVersion) !== -1) { if (res.Error) { protocolError = signalR._.error(signalR._.format(resources.errorFromServer, res.Error)); $(connection).triggerHandler(events.onError, [protocolError]); diff --git a/test/Microsoft.AspNet.SignalR.Client.JS.Tests/wwwroot/Tests/FunctionalTests/Core/RedirectFacts.js b/test/Microsoft.AspNet.SignalR.Client.JS.Tests/wwwroot/Tests/FunctionalTests/Core/RedirectFacts.js index ade1a8a55e..461558b329 100644 --- a/test/Microsoft.AspNet.SignalR.Client.JS.Tests/wwwroot/Tests/FunctionalTests/Core/RedirectFacts.js +++ b/test/Microsoft.AspNet.SignalR.Client.JS.Tests/wwwroot/Tests/FunctionalTests/Core/RedirectFacts.js @@ -263,7 +263,7 @@ QUnit.asyncTimeoutTest("Limits redirects", testUtilities.defaultTestTimeout, fun }); }); -QUnit.asyncTimeoutTest("Does not follow redirect url if ProtocolVersion is not 2.0", testUtilities.defaultTestTimeout, function (end, assert, testName) { +QUnit.asyncTimeoutTest("Does not follow redirect url if ProtocolVersion is less than 2.0", testUtilities.defaultTestTimeout, function (end, assert, testName) { var connection = testUtilities.createTestConnection(testName, end, assert, { url: "redirect-old-proto", ignoreErrors: true, hub: true, wrapStart: false }), proxies = connection.createHubProxies(), hub = proxies.redirectTestHub; @@ -277,6 +277,41 @@ QUnit.asyncTimeoutTest("Does not follow redirect url if ProtocolVersion is not 2 }); }); +QUnit.asyncTimeoutTest("Does not follow redirect url if ProtocolVersion is greater than 2.1", testUtilities.defaultTestTimeout, function (end, assert, testName) { + var connection = testUtilities.createTestConnection(testName, end, assert, { url: "redirect-future-proto", ignoreErrors: true, hub: true, wrapStart: false }), + proxies = connection.createHubProxies(), + hub = proxies.redirectTestHub; + + connection.start().done(function () { + assert.fail("Connection should fail!"); + end(); + }).fail(function (e) { + assert.equal(e.message, "You are using a version of the client that isn't compatible with the server. Client version 2.1, server version 2.2.", "Connection ignored RedirectUrl from new ProtocolVersion response."); + end(); + }); +}) + +QUnit.asyncTimeoutTest("Does follow redirect url if ProtocolVersion is 2.1", testUtilities.defaultTestTimeout, function (end, assert, testName) { + var connection = testUtilities.createTestConnection(testName, end, assert, { url: "redirect-new-proto", hub: true }), + proxies = connection.createHubProxies(), + hub = proxies.redirectTestHub; + + connection.start().done(function () { + assert.comment("Connection succeeded"); + + hub.server.echoReturn("Test Message").then(function (response) { + assert.equal(response, "Test Message", "Successfully called a server method"); + end(); + }).fail(function () { + assert.fail("Invocation failed!"); + end(); + }); + }).fail(function () { + assert.fail("Connection failed!"); + end(); + }); +}); + testUtilities.runWithAllTransports(function (transport) { if (transport === "foreverFrame") { // Forever Frame does not support connections that require a Bearer token to connect. diff --git a/test/Microsoft.AspNet.SignalR.FunctionalTests/Client/NegotiateFacts.cs b/test/Microsoft.AspNet.SignalR.FunctionalTests/Client/NegotiateFacts.cs index fc1a7c1497..044f0c4ff3 100644 --- a/test/Microsoft.AspNet.SignalR.FunctionalTests/Client/NegotiateFacts.cs +++ b/test/Microsoft.AspNet.SignalR.FunctionalTests/Client/NegotiateFacts.cs @@ -270,7 +270,7 @@ public async Task RedirectsAreLimitedToPreventInfiniteLooping() } [Fact] - public async Task DoesNotFollowRedirectIfProtocolVersionIsNot20() + public async Task DoesNotFollowRedirectIfProtocolVersionIsLessThan20() { using (var host = CreateHost(HostType.Memory, TransportType.Auto)) { @@ -284,6 +284,42 @@ public async Task DoesNotFollowRedirectIfProtocolVersionIsNot20() } } + [Fact] + public async Task DoesNotFollowRedirectIfProtocolVersionIsGreaterThan21() + { + using (var host = CreateHost(HostType.Memory, TransportType.Auto)) + { + host.Initialize(); + + using (var connection = CreateHubConnection(host, path: "/redirect-future-proto", useDefaultUrl: false)) + { + // Should fail to connect. + await Assert.ThrowsAsync(() => connection.Start(host.TransportFactory())); + } + } + } + + [Fact] + public async Task DoesFollowRedirectIfProtocolVersionIs21() + { + using (var host = CreateHost(HostType.Memory, TransportType.Auto)) + { + host.Initialize(); + + using (var connection = CreateHubConnection(host, path: "/redirect-new-proto", useDefaultUrl: false)) + { + var hub = connection.CreateHubProxy("RedirectTestHub"); + + await connection.Start(host.TransportFactory()); + + // Verify we're connected by calling the echo method + var result = await hub.Invoke("EchoReturn", "Hello, World!"); + + Assert.Equal("Hello, World!", result); + } + } + } + [Fact] public async Task ThrowsErrorProvidedByServerIfNegotiateResponseContainsErrorMessage() { diff --git a/test/Microsoft.AspNet.SignalR.Tests.Common/App_Start/Initializer.cs b/test/Microsoft.AspNet.SignalR.Tests.Common/App_Start/Initializer.cs index 47b6d06006..a265a8fda3 100644 --- a/test/Microsoft.AspNet.SignalR.Tests.Common/App_Start/Initializer.cs +++ b/test/Microsoft.AspNet.SignalR.Tests.Common/App_Start/Initializer.cs @@ -200,6 +200,8 @@ public static void ConfigureRoutes(IAppBuilder app, IDependencyResolver resolver // Wrong protocol version AppBuilderUseExtensions.Use(app, CreateRedirector("/redirect-old-proto", "/signalr", protocolVersion: "1.5")); + AppBuilderUseExtensions.Use(app, CreateRedirector("/redirect-new-proto", "/signalr", protocolVersion: "2.1")); + AppBuilderUseExtensions.Use(app, CreateRedirector("/redirect-future-proto", "/signalr", protocolVersion: "2.2")); } private static void RegisterSignalREndpoints(IAppBuilder app, IDependencyResolver resolver, HubConfiguration hubConfig)