Set SO_REUSEADDR on managed listen sockets on Unix (#2111)
- https://github.com/dotnet/corefx/issues/24562
This commit is contained in:
parent
deed6c9780
commit
66a3c9496a
|
|
@ -6,10 +6,12 @@ using System.Diagnostics;
|
||||||
using System.IO.Pipelines;
|
using System.IO.Pipelines;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Net.Sockets;
|
using System.Net.Sockets;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Protocols;
|
using Microsoft.AspNetCore.Protocols;
|
||||||
using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal;
|
using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal;
|
||||||
using Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets.Internal;
|
using Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets.Internal;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets
|
namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets
|
||||||
{
|
{
|
||||||
|
|
@ -51,6 +53,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets
|
||||||
|
|
||||||
var listenSocket = new Socket(endPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
|
var listenSocket = new Socket(endPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
|
||||||
|
|
||||||
|
EnableRebinding(listenSocket);
|
||||||
|
|
||||||
// Kestrel expects IPv6Any to bind to both IPv6 and IPv4
|
// Kestrel expects IPv6Any to bind to both IPv6 and IPv4
|
||||||
if (endPoint.Address == IPAddress.IPv6Any)
|
if (endPoint.Address == IPAddress.IPv6Any)
|
||||||
{
|
{
|
||||||
|
|
@ -128,5 +132,37 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[DllImport("libc", SetLastError = true)]
|
||||||
|
private static extern int setsockopt(IntPtr socket, int level, int option_name, IntPtr option_value, uint option_len);
|
||||||
|
|
||||||
|
private const int SOL_SOCKET_OSX = 0xffff;
|
||||||
|
private const int SO_REUSEADDR_OSX = 0x0004;
|
||||||
|
private const int SOL_SOCKET_LINUX = 0x0001;
|
||||||
|
private const int SO_REUSEADDR_LINUX = 0x0002;
|
||||||
|
|
||||||
|
// Without setting SO_REUSEADDR on macOS and Linux, binding to a recently used endpoint can fail.
|
||||||
|
// https://github.com/dotnet/corefx/issues/24562
|
||||||
|
private unsafe void EnableRebinding(Socket listenSocket)
|
||||||
|
{
|
||||||
|
var optionValue = 1;
|
||||||
|
var setsockoptStatus = 0;
|
||||||
|
|
||||||
|
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
|
||||||
|
{
|
||||||
|
setsockoptStatus = setsockopt(listenSocket.Handle, SOL_SOCKET_LINUX, SO_REUSEADDR_LINUX,
|
||||||
|
(IntPtr)(&optionValue), sizeof(int));
|
||||||
|
}
|
||||||
|
else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
|
||||||
|
{
|
||||||
|
setsockoptStatus = setsockopt(listenSocket.Handle, SOL_SOCKET_OSX, SO_REUSEADDR_OSX,
|
||||||
|
(IntPtr)(&optionValue), sizeof(int));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (setsockoptStatus != 0)
|
||||||
|
{
|
||||||
|
_trace.LogInformation("Setting SO_REUSEADDR failed with errno '{errno}'.", Marshal.GetLastWin32Error());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -526,10 +526,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://github.com/dotnet/corefx/issues/24562
|
[Fact]
|
||||||
[ConditionalFact]
|
|
||||||
[OSSkipCondition(OperatingSystems.MacOSX, SkipReason = "Sockets transport fails to rebind on macOS.")]
|
|
||||||
[OSSkipCondition(OperatingSystems.Linux, SkipReason = "Sockets transport fails to rebind on Linux.")]
|
|
||||||
public async Task CanRebindToEndPoint()
|
public async Task CanRebindToEndPoint()
|
||||||
{
|
{
|
||||||
var port = GetNextPort();
|
var port = GetNextPort();
|
||||||
|
|
@ -566,10 +563,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://github.com/dotnet/corefx/issues/24562
|
|
||||||
[ConditionalFact]
|
[ConditionalFact]
|
||||||
[OSSkipCondition(OperatingSystems.MacOSX, SkipReason = "Sockets transport fails to rebind on macOS.")]
|
[IPv6SupportedCondition]
|
||||||
[OSSkipCondition(OperatingSystems.Linux, SkipReason = "Sockets transport fails to rebind on Linux.")]
|
|
||||||
public async Task CanRebindToMultipleEndPoints()
|
public async Task CanRebindToMultipleEndPoints()
|
||||||
{
|
{
|
||||||
var port = GetNextPort();
|
var port = GetNextPort();
|
||||||
|
|
@ -892,12 +887,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
|
||||||
|
|
||||||
private bool CanBindToPort()
|
private bool CanBindToPort()
|
||||||
{
|
{
|
||||||
#if MACOS && SOCKETS
|
|
||||||
// Binding to a port with a Socket, disposing the Socket, and rebinding often fails with
|
|
||||||
// SocketError.AddressAlreadyInUse on macOS. This isn't an issue if binding with libuv second.
|
|
||||||
// https://github.com/dotnet/corefx/issues/24562
|
|
||||||
return false;
|
|
||||||
#else
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
using (var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp))
|
using (var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp))
|
||||||
|
|
@ -910,7 +899,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue