Set SO_REUSEADDR on managed listen sockets on Unix (#2111)

- https://github.com/dotnet/corefx/issues/24562
This commit is contained in:
Stephen Halter 2017-10-12 12:26:52 -07:00 committed by GitHub
parent deed6c9780
commit 66a3c9496a
2 changed files with 38 additions and 14 deletions

View File

@ -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());
}
}
} }
} }

View File

@ -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
} }
} }
} }