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.Net;
using System.Net.Sockets;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Protocols;
using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal;
using Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets.Internal;
using Microsoft.Extensions.Logging;
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);
EnableRebinding(listenSocket);
// Kestrel expects IPv6Any to bind to both IPv6 and IPv4
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
[ConditionalFact]
[OSSkipCondition(OperatingSystems.MacOSX, SkipReason = "Sockets transport fails to rebind on macOS.")]
[OSSkipCondition(OperatingSystems.Linux, SkipReason = "Sockets transport fails to rebind on Linux.")]
[Fact]
public async Task CanRebindToEndPoint()
{
var port = GetNextPort();
@ -566,10 +563,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
}
}
// https://github.com/dotnet/corefx/issues/24562
[ConditionalFact]
[OSSkipCondition(OperatingSystems.MacOSX, SkipReason = "Sockets transport fails to rebind on macOS.")]
[OSSkipCondition(OperatingSystems.Linux, SkipReason = "Sockets transport fails to rebind on Linux.")]
[IPv6SupportedCondition]
public async Task CanRebindToMultipleEndPoints()
{
var port = GetNextPort();
@ -892,12 +887,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
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
{
using (var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp))
@ -910,7 +899,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{
return false;
}
#endif
}
}
}