Added support for unix domain sockets to the sockets transport (#10560)
- Added a test for UnixDomainSockets on both transports
This commit is contained in:
parent
b2fa47d379
commit
ecacf90c7f
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Security.Cryptography.X509Certificates;
|
using System.Security.Cryptography.X509Certificates;
|
||||||
|
|
@ -295,7 +296,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException(nameof(socketPath));
|
throw new ArgumentNullException(nameof(socketPath));
|
||||||
}
|
}
|
||||||
if (socketPath.Length == 0 || socketPath[0] != '/')
|
|
||||||
|
if (!Path.IsPathRooted(socketPath))
|
||||||
{
|
{
|
||||||
throw new ArgumentException(CoreStrings.UnixSocketPathMustBeAbsolute, nameof(socketPath));
|
throw new ArgumentException(CoreStrings.UnixSocketPathMustBeAbsolute, nameof(socketPath));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -31,10 +31,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets
|
||||||
SocketTransportOptions options,
|
SocketTransportOptions options,
|
||||||
ISocketsTrace trace)
|
ISocketsTrace trace)
|
||||||
{
|
{
|
||||||
Debug.Assert(endpoint != null);
|
|
||||||
Debug.Assert(endpoint is IPEndPoint);
|
|
||||||
Debug.Assert(trace != null);
|
|
||||||
|
|
||||||
EndPoint = endpoint;
|
EndPoint = endpoint;
|
||||||
_trace = trace;
|
_trace = trace;
|
||||||
_options = options;
|
_options = options;
|
||||||
|
|
@ -66,9 +62,12 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets
|
||||||
throw new InvalidOperationException(SocketsStrings.TransportAlreadyBound);
|
throw new InvalidOperationException(SocketsStrings.TransportAlreadyBound);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Add support for UnixDomainSocket
|
Socket listenSocket;
|
||||||
|
|
||||||
var listenSocket = new Socket(EndPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
|
// 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
|
// Kestrel expects IPv6Any to bind to both IPv6 and IPv4
|
||||||
if (EndPoint is IPEndPoint ip && ip.Address == IPAddress.IPv6Any)
|
if (EndPoint is IPEndPoint ip && ip.Address == IPAddress.IPv6Any)
|
||||||
|
|
@ -99,7 +98,12 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var acceptSocket = await _listenSocket.AcceptAsync();
|
var acceptSocket = await _listenSocket.AcceptAsync();
|
||||||
acceptSocket.NoDelay = _options.NoDelay;
|
|
||||||
|
// Only apply no delay to Tcp based endpoints
|
||||||
|
if (acceptSocket.LocalEndPoint is IPEndPoint)
|
||||||
|
{
|
||||||
|
acceptSocket.NoDelay = _options.NoDelay;
|
||||||
|
}
|
||||||
|
|
||||||
var connection = new SocketConnection(acceptSocket, _memoryPool, _schedulers[_schedulerIndex], _trace, _options.MaxReadBufferSize, _options.MaxWriteBufferSize);
|
var connection = new SocketConnection(acceptSocket, _memoryPool, _schedulers[_schedulerIndex], _trace, _options.MaxReadBufferSize, _options.MaxWriteBufferSize);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,117 @@
|
||||||
|
// 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.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net.Sockets;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNetCore.Builder;
|
||||||
|
using Microsoft.AspNetCore.Connections;
|
||||||
|
using Microsoft.AspNetCore.Connections.Features;
|
||||||
|
using Microsoft.AspNetCore.Hosting;
|
||||||
|
using Microsoft.AspNetCore.Testing;
|
||||||
|
using Microsoft.AspNetCore.Testing.xunit;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
|
||||||
|
{
|
||||||
|
public class UnixDomainSocketsTest : TestApplicationErrorLoggerLoggedTest
|
||||||
|
{
|
||||||
|
#if LIBUV
|
||||||
|
[OSSkipCondition(OperatingSystems.Windows, SkipReason = "Libuv does not support unix domain sockets on Windows.")]
|
||||||
|
#else
|
||||||
|
[OSSkipCondition(OperatingSystems.Windows, WindowsVersions.Win7, WindowsVersions.Win8, WindowsVersions.Win81, WindowsVersions.Win2008R2, SkipReason = "UnixDomainSocketEndPoint is not supported on older versions of Windows")]
|
||||||
|
#endif
|
||||||
|
[ConditionalFact]
|
||||||
|
public async Task TestUnixDomainSocket()
|
||||||
|
{
|
||||||
|
var path = Path.GetTempFileName();
|
||||||
|
|
||||||
|
Delete(path);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
async Task EchoServer(ConnectionContext connection)
|
||||||
|
{
|
||||||
|
// For graceful shutdown
|
||||||
|
var notificationFeature = connection.Features.Get<IConnectionLifetimeNotificationFeature>();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
var result = await connection.Transport.Input.ReadAsync(notificationFeature.ConnectionClosedRequested);
|
||||||
|
|
||||||
|
if (result.IsCompleted)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
await connection.Transport.Output.WriteAsync(result.Buffer.ToArray());
|
||||||
|
|
||||||
|
connection.Transport.Input.AdvanceTo(result.Buffer.End);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (OperationCanceledException)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var hostBuilder = TransportSelector.GetWebHostBuilder()
|
||||||
|
.UseKestrel(o =>
|
||||||
|
{
|
||||||
|
o.ListenUnixSocket(path, builder =>
|
||||||
|
{
|
||||||
|
builder.Run(EchoServer);
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.ConfigureServices(AddTestLogging)
|
||||||
|
.Configure(c => { });
|
||||||
|
|
||||||
|
using (var host = hostBuilder.Build())
|
||||||
|
{
|
||||||
|
await host.StartAsync();
|
||||||
|
|
||||||
|
using (var socket = new Socket(AddressFamily.Unix, SocketType.Stream, ProtocolType.Unspecified))
|
||||||
|
{
|
||||||
|
await socket.ConnectAsync(new UnixDomainSocketEndPoint(path));
|
||||||
|
|
||||||
|
var data = Encoding.ASCII.GetBytes("Hello World");
|
||||||
|
await socket.SendAsync(data, SocketFlags.None);
|
||||||
|
|
||||||
|
var buffer = new byte[data.Length];
|
||||||
|
var read = 0;
|
||||||
|
while (read < data.Length)
|
||||||
|
{
|
||||||
|
read += await socket.ReceiveAsync(buffer.AsMemory(read, buffer.Length - read), SocketFlags.None);
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert.Equal(data, buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
await host.StopAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
Delete(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void Delete(string path)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
File.Delete(path);
|
||||||
|
}
|
||||||
|
catch (FileNotFoundException)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue