diff --git a/src/Servers/Connections.Abstractions/src/Features/IConnectionSocketFeature.cs b/src/Servers/Connections.Abstractions/src/Features/IConnectionSocketFeature.cs
new file mode 100644
index 000000000000..bef71a70b6ed
--- /dev/null
+++ b/src/Servers/Connections.Abstractions/src/Features/IConnectionSocketFeature.cs
@@ -0,0 +1,18 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Net.Sockets;
+
+namespace Microsoft.AspNetCore.Connections.Features
+{
+ ///
+ /// Provides access to the connection's underlying if any.
+ ///
+ public interface IConnectionSocketFeature
+ {
+ ///
+ /// Gets the underlying .
+ ///
+ Socket? Socket { get; }
+ }
+}
diff --git a/src/Servers/Connections.Abstractions/src/PublicAPI.Unshipped.txt b/src/Servers/Connections.Abstractions/src/PublicAPI.Unshipped.txt
index 575c538f6a9b..f79889680abe 100644
--- a/src/Servers/Connections.Abstractions/src/PublicAPI.Unshipped.txt
+++ b/src/Servers/Connections.Abstractions/src/PublicAPI.Unshipped.txt
@@ -1,5 +1,7 @@
#nullable enable
*REMOVED*Microsoft.AspNetCore.Connections.IConnectionListener.AcceptAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask
+Microsoft.AspNetCore.Connections.Features.IConnectionSocketFeature
+Microsoft.AspNetCore.Connections.Features.IConnectionSocketFeature.Socket.get -> System.Net.Sockets.Socket?
Microsoft.AspNetCore.Connections.IConnectionListener.AcceptAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask
Microsoft.AspNetCore.Connections.Experimental.IMultiplexedConnectionBuilder
Microsoft.AspNetCore.Connections.Experimental.IMultiplexedConnectionBuilder.ApplicationServices.get -> System.IServiceProvider!
diff --git a/src/Servers/Kestrel/Transport.Sockets/src/Internal/SocketConnection.cs b/src/Servers/Kestrel/Transport.Sockets/src/Internal/SocketConnection.cs
index d72ccc50f7a2..3b4e0cd800ef 100644
--- a/src/Servers/Kestrel/Transport.Sockets/src/Internal/SocketConnection.cs
+++ b/src/Servers/Kestrel/Transport.Sockets/src/Internal/SocketConnection.cs
@@ -51,6 +51,7 @@ internal SocketConnection(Socket socket,
MemoryPool = memoryPool;
_trace = trace;
_waitForData = waitForData;
+ Socket = socket;
_socketSenderPool = socketSenderPool;
LocalEndPoint = _socket.LocalEndPoint;
diff --git a/src/Servers/Kestrel/shared/TransportConnection.FeatureCollection.cs b/src/Servers/Kestrel/shared/TransportConnection.FeatureCollection.cs
index 757927e2f433..ce603419c949 100644
--- a/src/Servers/Kestrel/shared/TransportConnection.FeatureCollection.cs
+++ b/src/Servers/Kestrel/shared/TransportConnection.FeatureCollection.cs
@@ -4,6 +4,7 @@
using System.Buffers;
using System.Collections.Generic;
using System.IO.Pipelines;
+using System.Net.Sockets;
using System.Threading;
using Microsoft.AspNetCore.Connections.Features;
@@ -38,6 +39,8 @@ CancellationToken IConnectionLifetimeFeature.ConnectionClosed
set => ConnectionClosed = value;
}
+ Socket? IConnectionSocketFeature.Socket => Socket;
+
void IConnectionLifetimeFeature.Abort() => Abort(new ConnectionAbortedException("The connection was aborted by the application via IConnectionLifetimeFeature.Abort()."));
}
}
diff --git a/src/Servers/Kestrel/shared/TransportConnection.Generated.cs b/src/Servers/Kestrel/shared/TransportConnection.Generated.cs
index 0f32b7d54549..ca396fbb182b 100644
--- a/src/Servers/Kestrel/shared/TransportConnection.Generated.cs
+++ b/src/Servers/Kestrel/shared/TransportConnection.Generated.cs
@@ -18,7 +18,8 @@ internal partial class TransportConnection : IFeatureCollection,
IConnectionTransportFeature,
IConnectionItemsFeature,
IMemoryPoolFeature,
- IConnectionLifetimeFeature
+ IConnectionLifetimeFeature,
+ IConnectionSocketFeature
{
// Implemented features
internal protected IConnectionIdFeature? _currentIConnectionIdFeature;
@@ -26,6 +27,7 @@ internal partial class TransportConnection : IFeatureCollection,
internal protected IConnectionItemsFeature? _currentIConnectionItemsFeature;
internal protected IMemoryPoolFeature? _currentIMemoryPoolFeature;
internal protected IConnectionLifetimeFeature? _currentIConnectionLifetimeFeature;
+ internal protected IConnectionSocketFeature? _currentIConnectionSocketFeature;
private int _featureRevision;
@@ -38,6 +40,7 @@ private void FastReset()
_currentIConnectionItemsFeature = this;
_currentIMemoryPoolFeature = this;
_currentIConnectionLifetimeFeature = this;
+ _currentIConnectionSocketFeature = this;
}
@@ -130,6 +133,10 @@ private void ExtraFeatureSet(Type key, object? value)
{
feature = _currentIConnectionLifetimeFeature;
}
+ else if (key == typeof(IConnectionSocketFeature))
+ {
+ feature = _currentIConnectionSocketFeature;
+ }
else if (MaybeExtra != null)
{
feature = ExtraFeatureGet(key);
@@ -162,6 +169,10 @@ private void ExtraFeatureSet(Type key, object? value)
{
_currentIConnectionLifetimeFeature = (IConnectionLifetimeFeature?)value;
}
+ else if (key == typeof(IConnectionSocketFeature))
+ {
+ _currentIConnectionSocketFeature = (IConnectionSocketFeature?)value;
+ }
else
{
ExtraFeatureSet(key, value);
@@ -196,6 +207,10 @@ private void ExtraFeatureSet(Type key, object? value)
{
feature = Unsafe.As(ref _currentIConnectionLifetimeFeature);
}
+ else if (typeof(TFeature) == typeof(IConnectionSocketFeature))
+ {
+ feature = Unsafe.As(ref _currentIConnectionSocketFeature);
+ }
else if (MaybeExtra != null)
{
feature = (TFeature?)(ExtraFeatureGet(typeof(TFeature)));
@@ -231,6 +246,10 @@ private void ExtraFeatureSet(Type key, object? value)
{
_currentIConnectionLifetimeFeature = Unsafe.As(ref feature);
}
+ else if (typeof(TFeature) == typeof(IConnectionSocketFeature))
+ {
+ _currentIConnectionSocketFeature = Unsafe.As(ref feature);
+ }
else
{
ExtraFeatureSet(typeof(TFeature), feature);
@@ -259,6 +278,10 @@ private IEnumerable> FastEnumerable()
{
yield return new KeyValuePair(typeof(IConnectionLifetimeFeature), _currentIConnectionLifetimeFeature);
}
+ if (_currentIConnectionSocketFeature != null)
+ {
+ yield return new KeyValuePair(typeof(IConnectionSocketFeature), _currentIConnectionSocketFeature);
+ }
if (MaybeExtra != null)
{
diff --git a/src/Servers/Kestrel/shared/TransportConnection.cs b/src/Servers/Kestrel/shared/TransportConnection.cs
index 8a1d2b7f16bf..bef728ee83be 100644
--- a/src/Servers/Kestrel/shared/TransportConnection.cs
+++ b/src/Servers/Kestrel/shared/TransportConnection.cs
@@ -1,12 +1,12 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-using System;
using System.Buffers;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO.Pipelines;
using System.Net;
+using System.Net.Sockets;
using System.Threading;
using Microsoft.AspNetCore.Http.Features;
@@ -65,6 +65,8 @@ public override string ConnectionId
}
}
+ public Socket? Socket { get; protected set; }
+
public override CancellationToken ConnectionClosed { get; set; }
// DO NOT remove this override to ConnectionContext.Abort. Doing so would cause
diff --git a/src/Servers/Kestrel/tools/CodeGenerator/TransportConnectionFeatureCollection.cs b/src/Servers/Kestrel/tools/CodeGenerator/TransportConnectionFeatureCollection.cs
index 60d00f404a2b..b6a762f34152 100644
--- a/src/Servers/Kestrel/tools/CodeGenerator/TransportConnectionFeatureCollection.cs
+++ b/src/Servers/Kestrel/tools/CodeGenerator/TransportConnectionFeatureCollection.cs
@@ -17,7 +17,8 @@ public static string GenerateFile()
"IConnectionTransportFeature",
"IConnectionItemsFeature",
"IMemoryPoolFeature",
- "IConnectionLifetimeFeature"
+ "IConnectionLifetimeFeature",
+ "IConnectionSocketFeature"
};
var usings = $@"