diff --git a/src/SignalR/clients/csharp/Client.Core/ref/Microsoft.AspNetCore.SignalR.Client.Core.netstandard2.0.cs b/src/SignalR/clients/csharp/Client.Core/ref/Microsoft.AspNetCore.SignalR.Client.Core.netstandard2.0.cs index 3c9a824b2ba5..84c7df734405 100644 --- a/src/SignalR/clients/csharp/Client.Core/ref/Microsoft.AspNetCore.SignalR.Client.Core.netstandard2.0.cs +++ b/src/SignalR/clients/csharp/Client.Core/ref/Microsoft.AspNetCore.SignalR.Client.Core.netstandard2.0.cs @@ -26,6 +26,7 @@ public partial class HubConnection public static readonly System.TimeSpan DefaultServerTimeout; public HubConnection(Microsoft.AspNetCore.SignalR.Client.IConnectionFactory connectionFactory, Microsoft.AspNetCore.SignalR.Protocol.IHubProtocol protocol, Microsoft.Extensions.Logging.ILoggerFactory loggerFactory) { } public HubConnection(Microsoft.AspNetCore.SignalR.Client.IConnectionFactory connectionFactory, Microsoft.AspNetCore.SignalR.Protocol.IHubProtocol protocol, System.IServiceProvider serviceProvider, Microsoft.Extensions.Logging.ILoggerFactory loggerFactory) { } + public string ConnectionId { get { throw null; } } public System.TimeSpan HandshakeTimeout { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } public System.TimeSpan KeepAliveInterval { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } public System.TimeSpan ServerTimeout { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } diff --git a/src/SignalR/clients/csharp/Client.Core/src/HubConnection.cs b/src/SignalR/clients/csharp/Client.Core/src/HubConnection.cs index cde0dea2a96e..c80966573080 100644 --- a/src/SignalR/clients/csharp/Client.Core/src/HubConnection.cs +++ b/src/SignalR/clients/csharp/Client.Core/src/HubConnection.cs @@ -54,6 +54,7 @@ public partial class HubConnection private long _nextActivationSendPing; private bool _disposed; private bool _hasInherentKeepAlive; + private string _connectionId; private CancellationToken _uploadStreamToken; @@ -116,6 +117,13 @@ public partial class HubConnection /// public TimeSpan HandshakeTimeout { get; set; } = DefaultHandshakeTimeout; + /// + /// Gets the connection's current Id. This value will be cleared when the connection is stopped and will have a new value every time the connection is (re)established. + /// This value will be null if the negotiation step is skipped via HttpConnectionOptions or if the WebSockets transport is explicitly specified because the + /// client skips negotiation in that case as well. + /// + public string ConnectionId => _connectionId; + /// /// Indicates the state of the to the server. /// @@ -338,6 +346,7 @@ private async Task StartAsyncCore(CancellationToken cancellationToken) // Start the connection var connection = await _connectionFactory.ConnectAsync(_protocol.TransferFormat); + _connectionId = connection.ConnectionId; var startingConnectionState = new ConnectionState(connection, this); _hasInherentKeepAlive = connection.Features.Get()?.HasInherentKeepAlive ?? false; @@ -405,6 +414,8 @@ private async Task StopAsyncCore(bool disposing) (_serviceProvider as IDisposable)?.Dispose(); _disposed = true; } + + _connectionId = null; } finally { diff --git a/src/SignalR/clients/csharp/Client/test/FunctionalTests/HubConnectionTests.cs b/src/SignalR/clients/csharp/Client/test/FunctionalTests/HubConnectionTests.cs index 1d403d4609a2..9ffe7b3eb99e 100644 --- a/src/SignalR/clients/csharp/Client/test/FunctionalTests/HubConnectionTests.cs +++ b/src/SignalR/clients/csharp/Client/test/FunctionalTests/HubConnectionTests.cs @@ -155,6 +155,41 @@ public async Task CanStopAndStartConnection(string protocolName, HttpTransportTy } } + [Theory] + [MemberData(nameof(HubProtocolsAndTransportsAndHubPaths))] + [LogLevel(LogLevel.Trace)] + public async Task CanAccessConnectionIdFromHubConnection(string protocolName, HttpTransportType transportType, string path) + { + var protocol = HubProtocols[protocolName]; + using (StartServer(out var server)) + { + var connection = CreateHubConnection(server.Url, path, transportType, protocol, LoggerFactory); + try + { + Assert.Null(connection.ConnectionId); + await connection.StartAsync().OrTimeout(); + var originalClientConnectionId = connection.ConnectionId; + var connectionIdFromServer = await connection.InvokeAsync(nameof(TestHub.GetCallerConnectionId)).OrTimeout(); + Assert.Equal(connection.ConnectionId, connectionIdFromServer); + await connection.StopAsync().OrTimeout(); + Assert.Null(connection.ConnectionId); + await connection.StartAsync().OrTimeout(); + connectionIdFromServer = await connection.InvokeAsync(nameof(TestHub.GetCallerConnectionId)).OrTimeout(); + Assert.NotEqual(originalClientConnectionId, connectionIdFromServer); + Assert.Equal(connection.ConnectionId, connectionIdFromServer); + } + catch (Exception ex) + { + LoggerFactory.CreateLogger().LogError(ex, "{ExceptionType} from test", ex.GetType().FullName); + throw; + } + finally + { + await connection.DisposeAsync().OrTimeout(); + } + } + } + [Theory] [MemberData(nameof(HubProtocolsAndTransportsAndHubPaths))] [LogLevel(LogLevel.Trace)] diff --git a/src/SignalR/clients/csharp/Client/test/FunctionalTests/Hubs.cs b/src/SignalR/clients/csharp/Client/test/FunctionalTests/Hubs.cs index af321348b5d8..53efb39162dd 100644 --- a/src/SignalR/clients/csharp/Client/test/FunctionalTests/Hubs.cs +++ b/src/SignalR/clients/csharp/Client/test/FunctionalTests/Hubs.cs @@ -36,6 +36,11 @@ public async Task CallHandlerThatDoesntExist() await Clients.Client(Context.ConnectionId).SendAsync("NoClientHandler"); } + public string GetCallerConnectionId() + { + return Context.ConnectionId; + } + public ChannelReader StreamEcho(ChannelReader source) => TestHubMethodsImpl.StreamEcho(source); public string GetUserIdentifier() @@ -110,6 +115,11 @@ public async Task CallHandlerThatDoesntExist() await Clients.Client(Context.ConnectionId).NoClientHandler(); } + public string GetCallerConnectionId() + { + return Context.ConnectionId; + } + public ChannelReader StreamEcho(ChannelReader source) => TestHubMethodsImpl.StreamEcho(source); } @@ -135,6 +145,11 @@ public async Task CallHandlerThatDoesntExist() await Clients.Client(Context.ConnectionId).NoClientHandler(); } + public string GetCallerConnectionId() + { + return Context.ConnectionId; + } + public ChannelReader StreamEcho(ChannelReader source) => TestHubMethodsImpl.StreamEcho(source); }