diff --git a/src/SignalR/clients/ts/FunctionalTests/TestHub.cs b/src/SignalR/clients/ts/FunctionalTests/TestHub.cs index 980a04653a..7853595e55 100644 --- a/src/SignalR/clients/ts/FunctionalTests/TestHub.cs +++ b/src/SignalR/clients/ts/FunctionalTests/TestHub.cs @@ -34,6 +34,11 @@ namespace FunctionalTests return message; } + public string GetCallerConnectionId() + { + return Context.ConnectionId; + } + public int GetNumRedirects() { return int.Parse(Context.GetHttpContext().Request.Query["numRedirects"]); diff --git a/src/SignalR/clients/ts/FunctionalTests/ts/HubConnectionTests.ts b/src/SignalR/clients/ts/FunctionalTests/ts/HubConnectionTests.ts index 7bd557f4fe..84278f0f14 100644 --- a/src/SignalR/clients/ts/FunctionalTests/ts/HubConnectionTests.ts +++ b/src/SignalR/clients/ts/FunctionalTests/ts/HubConnectionTests.ts @@ -782,6 +782,9 @@ describe("hubConnection", () => { expect(newConnectionId).not.toBe(initialConnectionId); + const serverConnectionId = await hubConnection.invoke("GetCallerConnectionId"); + expect(newConnectionId).toBe(serverConnectionId); + const postReconnectRedirects = await hubConnection.invoke("GetNumRedirects"); expect(postReconnectRedirects).toBeGreaterThan(preReconnectRedirects); @@ -838,6 +841,79 @@ describe("hubConnection", () => { } }); + it("connection id matches server side connection id", async (done) => { + try { + const reconnectingPromise = new PromiseSource(); + const reconnectedPromise = new PromiseSource(); + const hubConnection = getConnectionBuilder(undefined, TESTHUB_REDIRECT_ENDPOINT_URL) + .withAutomaticReconnect() + .build(); + + hubConnection.onreconnecting(() => { + reconnectingPromise.resolve(); + }); + + hubConnection.onreconnected((connectionId?) => { + reconnectedPromise.resolve(connectionId); + }); + + expect(hubConnection.connectionId).toBeNull(); + + await hubConnection.start(); + + expect(hubConnection.connectionId).not.toBeNull(); + let serverConnectionId = await hubConnection.invoke("GetCallerConnectionId"); + expect(hubConnection.connectionId).toBe(serverConnectionId); + + const initialConnectionId = hubConnection.connectionId!; + + // Induce reconnect + (hubConnection as any).serverTimeout(); + + await reconnectingPromise; + const newConnectionId = await reconnectedPromise; + + expect(newConnectionId).not.toBe(initialConnectionId); + + serverConnectionId = await hubConnection.invoke("GetCallerConnectionId"); + expect(newConnectionId).toBe(serverConnectionId); + + await hubConnection.stop(); + expect(hubConnection.connectionId).toBeNull(); + + done(); + } catch (err) { + fail(err); + done(); + } + }); + + it("connection id is alwys null is negotiation is skipped", async (done) => { + try { + const hubConnection = getConnectionBuilder( + HttpTransportType.WebSockets, + undefined, + { skipNegotiation: true }, + ) + .build(); + + expect(hubConnection.connectionId).toBeNull(); + + await hubConnection.start(); + + expect(hubConnection.connectionId).toBeNull(); + + await hubConnection.stop(); + + expect(hubConnection.connectionId).toBeNull(); + + done(); + } catch (err) { + fail(err); + done(); + } + }); + if (typeof EventSource !== "undefined") { it("allows Server-Sent Events when negotiating for JSON protocol", async (done) => { const hubConnection = getConnectionBuilder(undefined, TESTHUB_NOWEBSOCKETS_ENDPOINT_URL) diff --git a/src/SignalR/clients/ts/signalr/src/HttpConnection.ts b/src/SignalR/clients/ts/signalr/src/HttpConnection.ts index cff6b0172f..729d01cc32 100644 --- a/src/SignalR/clients/ts/signalr/src/HttpConnection.ts +++ b/src/SignalR/clients/ts/signalr/src/HttpConnection.ts @@ -450,6 +450,7 @@ export class HttpConnection implements IConnection { this.logger.log(LogLevel.Information, "Connection disconnected."); } + this.connectionId = undefined; this.connectionState = ConnectionState.Disconnected; if (this.onclose && this.connectionStarted) { diff --git a/src/SignalR/clients/ts/signalr/src/HubConnection.ts b/src/SignalR/clients/ts/signalr/src/HubConnection.ts index baab33767d..b9421e025d 100644 --- a/src/SignalR/clients/ts/signalr/src/HubConnection.ts +++ b/src/SignalR/clients/ts/signalr/src/HubConnection.ts @@ -120,6 +120,13 @@ export class HubConnection { return this.connectionState; } + /** Represents the connection id of the {@link HubConnection} on the server. The connection id will be null when the connection is either + * in the disconnected state or if the negotiation step was skipped. + */ + get connectionId(): string | null { + return this.connection ? (this.connection.connectionId || null) : null; + } + /** Starts the connection. * * @returns {Promise} A Promise that resolves when the connection has been successfully established, or rejects with an error.