Add support for listening to tcp handles to in the Sockets transport (#22618)

* Add support for listening to tcp handles to in the Sockets transport
- Make ListenHandleTests shared across both transports

Fixes #20842
This commit is contained in:
David Fowler 2020-06-11 09:36:27 -07:00 committed by GitHub
parent b61e9b0125
commit 9e55787b84
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 41 additions and 30 deletions

View File

@ -3,7 +3,6 @@
using System;
using System.Buffers;
using System.Diagnostics;
using System.IO.Pipelines;
using System.Net;
using System.Net.Sockets;
@ -23,6 +22,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets
private Socket _listenSocket;
private int _schedulerIndex;
private readonly SocketTransportOptions _options;
private SafeSocketHandle _socketHandle;
public EndPoint EndPoint { get; private set; }
@ -62,33 +62,44 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets
throw new InvalidOperationException(SocketsStrings.TransportAlreadyBound);
}
// Check if EndPoint is a FileHandleEndpoint before attempting to access EndPoint.AddressFamily
// since that will throw an NotImplementedException.
if (EndPoint is FileHandleEndPoint)
{
throw new NotSupportedException(SocketsStrings.FileHandleEndPointNotSupported);
}
Socket listenSocket;
// Unix domain sockets are unspecified
var protocolType = EndPoint is UnixDomainSocketEndPoint ? ProtocolType.Unspecified : ProtocolType.Tcp;
listenSocket = new Socket(EndPoint.AddressFamily, SocketType.Stream, protocolType);
// Kestrel expects IPv6Any to bind to both IPv6 and IPv4
if (EndPoint is IPEndPoint ip && ip.Address == IPAddress.IPv6Any)
switch (EndPoint)
{
listenSocket.DualMode = true;
case FileHandleEndPoint fileHandle:
_socketHandle = new SafeSocketHandle((IntPtr)fileHandle.FileHandle, ownsHandle: true);
listenSocket = new Socket(_socketHandle);
break;
case UnixDomainSocketEndPoint unix:
listenSocket = new Socket(unix.AddressFamily, SocketType.Stream, ProtocolType.Unspecified);
BindSocket();
break;
case IPEndPoint ip:
listenSocket = new Socket(ip.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
// Kestrel expects IPv6Any to bind to both IPv6 and IPv4
if (ip.Address == IPAddress.IPv6Any)
{
listenSocket.DualMode = true;
}
BindSocket();
break;
default:
listenSocket = new Socket(EndPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
BindSocket();
break;
}
try
void BindSocket()
{
listenSocket.Bind(EndPoint);
}
catch (SocketException e) when (e.SocketErrorCode == SocketError.AddressAlreadyInUse)
{
throw new AddressInUseException(e.Message, e);
try
{
listenSocket.Bind(EndPoint);
}
catch (SocketException e) when (e.SocketErrorCode == SocketError.AddressAlreadyInUse)
{
throw new AddressInUseException(e.Message, e);
}
}
EndPoint = listenSocket.LocalEndPoint;
@ -142,12 +153,17 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets
public ValueTask UnbindAsync(CancellationToken cancellationToken = default)
{
_listenSocket?.Dispose();
_socketHandle?.Dispose();
return default;
}
public ValueTask DisposeAsync()
{
_listenSocket?.Dispose();
_socketHandle?.Dispose();
// Dispose the memory pool
_memoryPool.Dispose();
return default;

View File

@ -1,12 +1,14 @@
// 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.Net;
using System.Net.Sockets;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Connections;
using Microsoft.AspNetCore.Server.Kestrel.Core;
using Microsoft.AspNetCore.Testing;
using Microsoft.Extensions.Logging.Testing;
using Xunit;
namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{

View File

@ -14,13 +14,6 @@ namespace Sockets.BindTests
{
public class SocketTransportFactoryTests
{
[Fact]
public async Task ThrowsNotSupportedExceptionWhenBindingToFileHandleEndPoint()
{
var socketTransportFactory = new SocketTransportFactory(Options.Create(new SocketTransportOptions()), Mock.Of<ILoggerFactory>());
await Assert.ThrowsAsync<NotSupportedException>(async () => await socketTransportFactory.BindAsync(new FileHandleEndPoint(0, FileHandleType.Auto)));
}
[Fact]
public async Task ThrowsNotImplementedExceptionWhenBindingToUriEndPoint()
{