diff --git a/src/Microsoft.AspNetCore.SignalR.Core/HubConnectionContext.cs b/src/Microsoft.AspNetCore.SignalR.Core/HubConnectionContext.cs index a03c17e9a2..0acb43c7f7 100644 --- a/src/Microsoft.AspNetCore.SignalR.Core/HubConnectionContext.cs +++ b/src/Microsoft.AspNetCore.SignalR.Core/HubConnectionContext.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; +using System.Net; using System.Runtime.ExceptionServices; using System.Security.Claims; using System.Threading; @@ -69,6 +70,14 @@ namespace Microsoft.AspNetCore.SignalR // Currently used only for streaming methods internal ConcurrentDictionary ActiveRequestCancellationSources { get; } = new ConcurrentDictionary(); + public IPAddress RemoteIpAddress => Features.Get()?.RemoteIpAddress; + + public IPAddress LocalIpAddress => Features.Get()?.LocalIpAddress; + + public int? RemotePort => Features.Get()?.RemotePort; + + public int? LocalPort => Features.Get()?.LocalPort; + public async Task WriteAsync(HubInvocationMessage message) { while (await Output.Writer.WaitToWriteAsync()) diff --git a/src/Microsoft.AspNetCore.Sockets.Http/HttpConnectionDispatcher.cs b/src/Microsoft.AspNetCore.Sockets.Http/HttpConnectionDispatcher.cs index 5ea1703fb8..e63944a7bd 100644 --- a/src/Microsoft.AspNetCore.Sockets.Http/HttpConnectionDispatcher.cs +++ b/src/Microsoft.AspNetCore.Sockets.Http/HttpConnectionDispatcher.cs @@ -8,6 +8,7 @@ using System.Text; using System.Threading; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.Features; using Microsoft.AspNetCore.Sockets.Features; using Microsoft.AspNetCore.Sockets.Internal; using Microsoft.AspNetCore.Sockets.Internal.Transports; @@ -459,6 +460,9 @@ namespace Microsoft.AspNetCore.Sockets return false; } + // Set the IHttpConnectionFeature now that we can access it. + connection.Features.Set(context.Features.Get()); + var transport = (TransportType?)connection.Metadata[ConnectionMetadataNames.Transport]; if (transport == null) diff --git a/test/Microsoft.AspNetCore.SignalR.Client.FunctionalTests/HubConnectionTests.cs b/test/Microsoft.AspNetCore.SignalR.Client.FunctionalTests/HubConnectionTests.cs index e9aa363f13..3f624232e9 100644 --- a/test/Microsoft.AspNetCore.SignalR.Client.FunctionalTests/HubConnectionTests.cs +++ b/test/Microsoft.AspNetCore.SignalR.Client.FunctionalTests/HubConnectionTests.cs @@ -757,6 +757,44 @@ namespace Microsoft.AspNetCore.SignalR.Client.FunctionalTests } } + [Theory] + [MemberData(nameof(TransportTypes))] + public async Task CheckHttpConnectionFeatures(TransportType transportType) + { + using (StartLog(out var loggerFactory, $"{nameof(CheckHttpConnectionFeatures)}_{transportType}")) + { + var hubConnection = new HubConnectionBuilder() + .WithUrl(_serverFixture.Url + "/default") + .WithTransport(transportType) + .WithLoggerFactory(loggerFactory) + .Build(); + try + { + await hubConnection.StartAsync().OrTimeout(); + + var features = await hubConnection.InvokeAsync("GetIHttpConnectionFeatureProperties").OrTimeout(); + var localPort = (Int64)features[0]; + var remotePort = (Int64)features[1]; + var localIP = (string)features[2]; + var remoteIP = (string)features[3]; + + Assert.True(localPort > 0L); + Assert.True(remotePort > 0L); + Assert.Equal("127.0.0.1", localIP); + Assert.Equal("127.0.0.1", remoteIP); + } + catch (Exception ex) + { + loggerFactory.CreateLogger().LogError(ex, "Exception from test"); + throw; + } + finally + { + await hubConnection.DisposeAsync().OrTimeout(); + } + } + } + public static IEnumerable HubProtocolsAndTransportsAndHubPaths { get diff --git a/test/Microsoft.AspNetCore.SignalR.Client.FunctionalTests/Hubs.cs b/test/Microsoft.AspNetCore.SignalR.Client.FunctionalTests/Hubs.cs index 35f291fd0f..6b9545d8a0 100644 --- a/test/Microsoft.AspNetCore.SignalR.Client.FunctionalTests/Hubs.cs +++ b/test/Microsoft.AspNetCore.SignalR.Client.FunctionalTests/Hubs.cs @@ -44,6 +44,19 @@ namespace Microsoft.AspNetCore.SignalR.Client.FunctionalTests { return Context.Connection.GetHttpContext().Request.Cookies[cookieName]; } + + public object[] GetIHttpConnectionFeatureProperties() + { + object[] result = + { + Context.Connection.LocalPort, + Context.Connection.RemotePort, + Context.Connection.LocalIpAddress.ToString(), + Context.Connection.RemoteIpAddress.ToString() + }; + + return result; + } } public class DynamicTestHub : DynamicHub diff --git a/test/Microsoft.AspNetCore.SignalR.Tests.Utils/ServerFixture.cs b/test/Microsoft.AspNetCore.SignalR.Tests.Utils/ServerFixture.cs index 0a8f6bc63a..bedc092b18 100644 --- a/test/Microsoft.AspNetCore.SignalR.Tests.Utils/ServerFixture.cs +++ b/test/Microsoft.AspNetCore.SignalR.Tests.Utils/ServerFixture.cs @@ -37,7 +37,8 @@ namespace Microsoft.AspNetCore.SignalR.Tests _logToken = testLog.StartTestLog(null, $"{nameof(ServerFixture)}_{typeof(TStartup).Name}", out _loggerFactory, "ServerFixture"); _loggerFactory.AddProvider(_asyncLoggerProvider); _logger = _loggerFactory.CreateLogger>(); - Url = "http://localhost:" + GetNextPort(); + // We're using 127.0.0.1 instead of localhost to ensure that we use IPV4 across different OSes + Url = "http://127.0.0.1:" + GetNextPort(); StartServer(Url); }