From f7304e1b6b78519f9e0b88d58bd79b53b46f9cfb Mon Sep 17 00:00:00 2001 From: Stephen Halter <halter73@gmail.com> Date: Thu, 5 Oct 2017 12:43:11 -0700 Subject: [PATCH 01/15] Make Sockets the default transport --- samples/SampleApp/Startup.cs | 5 -- src/Kestrel/Kestrel.csproj | 2 +- .../WebHostBuilderKestrelExtensions.cs | 2 +- .../ThreadCountTests.cs | 76 +++++++++---------- 4 files changed, 40 insertions(+), 45 deletions(-) diff --git a/samples/SampleApp/Startup.cs b/samples/SampleApp/Startup.cs index e89e97790..46758796e 100644 --- a/samples/SampleApp/Startup.cs +++ b/samples/SampleApp/Startup.cs @@ -80,11 +80,6 @@ public static Task Main(string[] args) // The following section should be used to demo sockets //options.ListenUnixSocket("/tmp/kestrel-test.sock"); }) - .UseLibuv(options => - { - // Uncomment the following line to change the default number of libuv threads for all endpoints. - // options.ThreadCount = 4; - }) .UseContentRoot(Directory.GetCurrentDirectory()) .UseStartup<Startup>() .Build(); diff --git a/src/Kestrel/Kestrel.csproj b/src/Kestrel/Kestrel.csproj index cdd0e1bc7..0cc57be49 100644 --- a/src/Kestrel/Kestrel.csproj +++ b/src/Kestrel/Kestrel.csproj @@ -16,7 +16,7 @@ <ItemGroup> <ProjectReference Include="..\Kestrel.Core\Kestrel.Core.csproj" /> - <ProjectReference Include="..\Kestrel.Transport.Libuv\Kestrel.Transport.Libuv.csproj" /> + <ProjectReference Include="..\Kestrel.Transport.Sockets\Kestrel.Transport.Sockets.csproj" /> </ItemGroup> </Project> diff --git a/src/Kestrel/WebHostBuilderKestrelExtensions.cs b/src/Kestrel/WebHostBuilderKestrelExtensions.cs index 3d3dad73c..6c28b16a2 100644 --- a/src/Kestrel/WebHostBuilderKestrelExtensions.cs +++ b/src/Kestrel/WebHostBuilderKestrelExtensions.cs @@ -23,7 +23,7 @@ public static class WebHostBuilderKestrelExtensions /// </returns> public static IWebHostBuilder UseKestrel(this IWebHostBuilder hostBuilder) { - hostBuilder.UseLibuv(); + hostBuilder.UseSockets(); return hostBuilder.ConfigureServices(services => { diff --git a/test/Kestrel.FunctionalTests/ThreadCountTests.cs b/test/Kestrel.FunctionalTests/ThreadCountTests.cs index 67f5cbafb..1fce8183e 100644 --- a/test/Kestrel.FunctionalTests/ThreadCountTests.cs +++ b/test/Kestrel.FunctionalTests/ThreadCountTests.cs @@ -14,47 +14,47 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests { public class ThreadCountTests { - [ConditionalTheory] - [MemberData(nameof(OneToTen))] - [OSSkipCondition(OperatingSystems.MacOSX, SkipReason = "Tests fail on OS X due to low file descriptor limit.")] - public async Task OneToTenThreads(int threadCount) - { - var hostBuilder = new WebHostBuilder() - .UseKestrel() - .UseLibuv(options => - { - options.ThreadCount = threadCount; - }) - .UseUrls("http://127.0.0.1:0/") - .Configure(app => - { - app.Run(context => - { - return context.Response.WriteAsync("Hello World"); - }); - }); + //[ConditionalTheory] + //[MemberData(nameof(OneToTen))] + //[OSSkipCondition(OperatingSystems.MacOSX, SkipReason = "Tests fail on OS X due to low file descriptor limit.")] + //public async Task OneToTenThreads(int threadCount) + //{ + // var hostBuilder = new WebHostBuilder() + // .UseKestrel() + // .UseLibuv(options => + // { + // options.ThreadCount = threadCount; + // }) + // .UseUrls("http://127.0.0.1:0/") + // .Configure(app => + // { + // app.Run(context => + // { + // return context.Response.WriteAsync("Hello World"); + // }); + // }); - using (var host = hostBuilder.Build()) - { - host.Start(); + // using (var host = hostBuilder.Build()) + // { + // host.Start(); - using (var client = new HttpClient()) - { - // Send 20 requests just to make sure we don't get any failures - var requestTasks = new List<Task<string>>(); - for (int i = 0; i < 20; i++) - { - var requestTask = client.GetStringAsync($"http://127.0.0.1:{host.GetPort()}/"); - requestTasks.Add(requestTask); - } + // using (var client = new HttpClient()) + // { + // // Send 20 requests just to make sure we don't get any failures + // var requestTasks = new List<Task<string>>(); + // for (int i = 0; i < 20; i++) + // { + // var requestTask = client.GetStringAsync($"http://127.0.0.1:{host.GetPort()}/"); + // requestTasks.Add(requestTask); + // } - foreach (var result in await Task.WhenAll(requestTasks)) - { - Assert.Equal("Hello World", result); - } - } - } - } + // foreach (var result in await Task.WhenAll(requestTasks)) + // { + // Assert.Equal("Hello World", result); + // } + // } + // } + //} public static TheoryData<int> OneToTen { From 05472f5a6104632be2f864323bf9437273519316 Mon Sep 17 00:00:00 2001 From: Stephen Halter <halter73@gmail.com> Date: Thu, 5 Oct 2017 12:43:49 -0700 Subject: [PATCH 02/15] Fix failing functional tests post Sockets transport default --- .../Internal/ISocketTrace.cs | 23 +++++ .../Internal/SocketTrace.cs | 93 +++++++++++++++++++ .../SocketConnection.cs | 31 ++++++- .../SocketTransport.cs | 12 ++- .../SocketTransportFactory.cs | 20 +++- test/Kestrel.FunctionalTests/RequestTests.cs | 12 ++- test/Kestrel.FunctionalTests/ResponseTests.cs | 1 + 7 files changed, 181 insertions(+), 11 deletions(-) create mode 100644 src/Kestrel.Transport.Sockets/Internal/ISocketTrace.cs create mode 100644 src/Kestrel.Transport.Sockets/Internal/SocketTrace.cs diff --git a/src/Kestrel.Transport.Sockets/Internal/ISocketTrace.cs b/src/Kestrel.Transport.Sockets/Internal/ISocketTrace.cs new file mode 100644 index 000000000..668a98d7d --- /dev/null +++ b/src/Kestrel.Transport.Sockets/Internal/ISocketTrace.cs @@ -0,0 +1,23 @@ +// 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 Microsoft.Extensions.Logging; + +namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets.Internal +{ + public interface ISocketTrace : ILogger + { + void ConnectionReadFin(string connectionId); + + void ConnectionWriteFin(string connectionId); + + void ConnectionError(string connectionId, Exception ex); + + void ConnectionReset(string connectionId); + + void ConnectionPause(string connectionId); + + void ConnectionResume(string connectionId); + } +} diff --git a/src/Kestrel.Transport.Sockets/Internal/SocketTrace.cs b/src/Kestrel.Transport.Sockets/Internal/SocketTrace.cs new file mode 100644 index 000000000..6270bed5c --- /dev/null +++ b/src/Kestrel.Transport.Sockets/Internal/SocketTrace.cs @@ -0,0 +1,93 @@ +// 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 Microsoft.Extensions.Logging; + +namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets.Internal +{ + public class SocketTrace : ISocketTrace + { + // ConnectionRead: Reserved: 3 + + private static readonly Action<ILogger, string, Exception> _connectionPause = + LoggerMessage.Define<string>(LogLevel.Debug, 4, @"Connection id ""{ConnectionId}"" paused."); + + private static readonly Action<ILogger, string, Exception> _connectionResume = + LoggerMessage.Define<string>(LogLevel.Debug, 5, @"Connection id ""{ConnectionId}"" resumed."); + + private static readonly Action<ILogger, string, Exception> _connectionReadFin = + LoggerMessage.Define<string>(LogLevel.Debug, 6, @"Connection id ""{ConnectionId}"" received FIN."); + + private static readonly Action<ILogger, string, Exception> _connectionWriteFin = + LoggerMessage.Define<string>(LogLevel.Debug, 7, @"Connection id ""{ConnectionId}"" sending FIN."); + + private static readonly Action<ILogger, string, Exception> _connectionError = + LoggerMessage.Define<string>(LogLevel.Information, 14, @"Connection id ""{ConnectionId}"" communication error."); + + private static readonly Action<ILogger, string, Exception> _connectionReset = + LoggerMessage.Define<string>(LogLevel.Debug, 19, @"Connection id ""{ConnectionId}"" reset."); + + private readonly ILogger _logger; + + public SocketTrace(ILogger logger) + { + _logger = logger; + } + + public void ConnectionRead(string connectionId, int count) + { + // Don't log for now since this could be *too* verbose. + // Reserved: Event ID 3 + } + + public void ConnectionReadFin(string connectionId) + { + _connectionReadFin(_logger, connectionId, null); + } + + public void ConnectionWriteFin(string connectionId) + { + _connectionWriteFin(_logger, connectionId, null); + } + + public void ConnectionWrite(string connectionId, int count) + { + // Don't log for now since this could be *too* verbose. + // Reserved: Event ID 11 + } + + public void ConnectionWriteCallback(string connectionId, int status) + { + // Don't log for now since this could be *too* verbose. + // Reserved: Event ID 12 + } + + public void ConnectionError(string connectionId, Exception ex) + { + _connectionError(_logger, connectionId, ex); + } + + public void ConnectionReset(string connectionId) + { + _connectionReset(_logger, connectionId, null); + } + + public void ConnectionPause(string connectionId) + { + _connectionPause(_logger, connectionId, null); + } + + public void ConnectionResume(string connectionId) + { + _connectionResume(_logger, connectionId, null); + } + + public IDisposable BeginScope<TState>(TState state) => _logger.BeginScope(state); + + public bool IsEnabled(LogLevel logLevel) => _logger.IsEnabled(logLevel); + + public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter) + => _logger.Log(logLevel, eventId, state, exception, formatter); + } +} diff --git a/src/Kestrel.Transport.Sockets/SocketConnection.cs b/src/Kestrel.Transport.Sockets/SocketConnection.cs index 4f11d7cd1..43a3428fa 100644 --- a/src/Kestrel.Transport.Sockets/SocketConnection.cs +++ b/src/Kestrel.Transport.Sockets/SocketConnection.cs @@ -5,12 +5,13 @@ using System.Collections.Generic; using System.Diagnostics; using System.IO; +using System.IO.Pipelines; using System.Net; using System.Net.Sockets; using System.Threading.Tasks; -using System.IO.Pipelines; -using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal; using Microsoft.AspNetCore.Protocols; +using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal; +using Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets.Internal; namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets { @@ -18,17 +19,20 @@ internal sealed class SocketConnection : TransportConnection { private readonly Socket _socket; private readonly SocketTransport _transport; + private readonly ISocketTrace _trace; private IList<ArraySegment<byte>> _sendBufferList; private const int MinAllocBufferSize = 2048; - internal SocketConnection(Socket socket, SocketTransport transport) + internal SocketConnection(Socket socket, SocketTransport transport, ISocketTrace trace) { Debug.Assert(socket != null); Debug.Assert(transport != null); + Debug.Assert(trace != null); _socket = socket; _transport = transport; + _trace = trace; var localEndPoint = (IPEndPoint)_socket.LocalEndPoint; var remoteEndPoint = (IPEndPoint)_socket.RemoteEndPoint; @@ -56,6 +60,7 @@ public async Task StartAsync(IConnectionHandler connectionHandler) if (await Task.WhenAny(receiveTask, sendTask) == sendTask) { // Tell the reader it's being aborted + _trace.ConnectionWriteFin(ConnectionId); _socket.Dispose(); } @@ -90,6 +95,7 @@ private async Task DoReceive() if (bytesReceived == 0) { // FIN + _trace.ConnectionReadFin(ConnectionId); break; } @@ -100,7 +106,18 @@ private async Task DoReceive() buffer.Commit(); } - var result = await buffer.FlushAsync(); + var flushTask = buffer.FlushAsync(); + + if (!flushTask.IsCompleted) + { + _trace.ConnectionPause(ConnectionId); + + await flushTask; + + _trace.ConnectionResume(ConnectionId); + } + + var result = flushTask.GetAwaiter().GetResult(); if (result.IsCompleted) { // Pipe consumer is shut down, do we stop writing @@ -111,22 +128,27 @@ private async Task DoReceive() catch (SocketException ex) when (ex.SocketErrorCode == SocketError.ConnectionReset) { error = new ConnectionResetException(ex.Message, ex); + _trace.ConnectionReset(ConnectionId); } catch (SocketException ex) when (ex.SocketErrorCode == SocketError.OperationAborted) { error = new ConnectionAbortedException(); + _trace.ConnectionError(ConnectionId, error); } catch (ObjectDisposedException) { error = new ConnectionAbortedException(); + _trace.ConnectionError(ConnectionId, error); } catch (IOException ex) { error = ex; + _trace.ConnectionError(ConnectionId, error); } catch (Exception ex) { error = new IOException(ex.Message, ex); + _trace.ConnectionError(ConnectionId, error); } finally { @@ -203,6 +225,7 @@ private async Task DoSend() } } + _trace.ConnectionWriteFin(ConnectionId); _socket.Shutdown(SocketShutdown.Send); } catch (SocketException ex) when (ex.SocketErrorCode == SocketError.OperationAborted) diff --git a/src/Kestrel.Transport.Sockets/SocketTransport.cs b/src/Kestrel.Transport.Sockets/SocketTransport.cs index 83a4cb290..ab2c69eef 100644 --- a/src/Kestrel.Transport.Sockets/SocketTransport.cs +++ b/src/Kestrel.Transport.Sockets/SocketTransport.cs @@ -8,6 +8,7 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Protocols; using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal; +using Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets.Internal; namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets { @@ -16,19 +17,26 @@ internal sealed class SocketTransport : ITransport private readonly SocketTransportFactory _transportFactory; private readonly IEndPointInformation _endPointInformation; private readonly IConnectionHandler _handler; + private readonly ISocketTrace _trace; private Socket _listenSocket; private Task _listenTask; - internal SocketTransport(SocketTransportFactory transportFactory, IEndPointInformation endPointInformation, IConnectionHandler handler) + internal SocketTransport( + SocketTransportFactory transportFactory, + IEndPointInformation endPointInformation, + IConnectionHandler handler, + ISocketTrace trace) { Debug.Assert(transportFactory != null); Debug.Assert(endPointInformation != null); Debug.Assert(endPointInformation.Type == ListenType.IPEndPoint); Debug.Assert(handler != null); + Debug.Assert(trace != null); _transportFactory = transportFactory; _endPointInformation = endPointInformation; _handler = handler; + _trace = trace; _listenSocket = null; _listenTask = null; @@ -105,7 +113,7 @@ private async Task RunAcceptLoopAsync() acceptSocket.NoDelay = _endPointInformation.NoDelay; - var connection = new SocketConnection(acceptSocket, this); + var connection = new SocketConnection(acceptSocket, this, _trace); _ = connection.StartAsync(_handler); } } diff --git a/src/Kestrel.Transport.Sockets/SocketTransportFactory.cs b/src/Kestrel.Transport.Sockets/SocketTransportFactory.cs index 849b16834..f4620951e 100644 --- a/src/Kestrel.Transport.Sockets/SocketTransportFactory.cs +++ b/src/Kestrel.Transport.Sockets/SocketTransportFactory.cs @@ -4,16 +4,32 @@ using System; using System.IO.Pipelines; using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal; +using Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets.Internal; using Microsoft.Extensions.Options; +using Microsoft.Extensions.Logging; namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets { public sealed class SocketTransportFactory : ITransportFactory { private readonly PipeFactory _pipeFactory = new PipeFactory(); + private readonly SocketTrace _trace; - public SocketTransportFactory(IOptions<SocketTransportOptions> options) + public SocketTransportFactory( + IOptions<SocketTransportOptions> options, + ILoggerFactory loggerFactory) { + if (options == null) + { + throw new ArgumentNullException(nameof(options)); + } + if (loggerFactory == null) + { + throw new ArgumentNullException(nameof(loggerFactory)); + } + + var logger = loggerFactory.CreateLogger("Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets"); + _trace = new SocketTrace(logger); } public ITransport Create(IEndPointInformation endPointInformation, IConnectionHandler handler) @@ -33,7 +49,7 @@ public ITransport Create(IEndPointInformation endPointInformation, IConnectionHa throw new ArgumentNullException(nameof(handler)); } - return new SocketTransport(this, endPointInformation, handler); + return new SocketTransport(this, endPointInformation, handler, _trace); } internal PipeFactory PipeFactory => _pipeFactory; diff --git a/test/Kestrel.FunctionalTests/RequestTests.cs b/test/Kestrel.FunctionalTests/RequestTests.cs index 1362651bd..e0563d398 100644 --- a/test/Kestrel.FunctionalTests/RequestTests.cs +++ b/test/Kestrel.FunctionalTests/RequestTests.cs @@ -300,7 +300,9 @@ public async Task ConnectionResetPriorToRequestIsLoggedAsDebug() .Setup(factory => factory.CreateLogger(It.IsAny<string>())) .Returns(Mock.Of<ILogger>()); mockLoggerFactory - .Setup(factory => factory.CreateLogger(It.IsIn("Microsoft.AspNetCore.Server.Kestrel", "Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv"))) + .Setup(factory => factory.CreateLogger(It.IsIn("Microsoft.AspNetCore.Server.Kestrel", + "Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv", + "Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets"))) .Returns(mockLogger.Object); using (var server = new TestServer(context => Task.CompletedTask, new TestServiceContext(mockLoggerFactory.Object))) @@ -354,7 +356,9 @@ public async Task ConnectionResetBetweenRequestsIsLoggedAsDebug() .Setup(factory => factory.CreateLogger(It.IsAny<string>())) .Returns(Mock.Of<ILogger>()); mockLoggerFactory - .Setup(factory => factory.CreateLogger(It.IsIn("Microsoft.AspNetCore.Server.Kestrel", "Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv"))) + .Setup(factory => factory.CreateLogger(It.IsIn("Microsoft.AspNetCore.Server.Kestrel", + "Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv", + "Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets"))) .Returns(mockLogger.Object); using (var server = new TestServer(context => Task.CompletedTask, new TestServiceContext(mockLoggerFactory.Object))) @@ -424,7 +428,9 @@ public async Task ConnectionResetMidRequestIsLoggedAsDebug() .Setup(factory => factory.CreateLogger(It.IsAny<string>())) .Returns(Mock.Of<ILogger>()); mockLoggerFactory - .Setup(factory => factory.CreateLogger(It.IsIn("Microsoft.AspNetCore.Server.Kestrel", "Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv"))) + .Setup(factory => factory.CreateLogger(It.IsIn("Microsoft.AspNetCore.Server.Kestrel", + "Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv", + "Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets"))) .Returns(mockLogger.Object); using (var server = new TestServer(async context => diff --git a/test/Kestrel.FunctionalTests/ResponseTests.cs b/test/Kestrel.FunctionalTests/ResponseTests.cs index d19ffbfb1..7dfa240da 100644 --- a/test/Kestrel.FunctionalTests/ResponseTests.cs +++ b/test/Kestrel.FunctionalTests/ResponseTests.cs @@ -2506,6 +2506,7 @@ public void ConnectionClosedWhenResponseDoesNotSatisfyMinimumDataRate() totalReceived += received; } while (received > 0 && totalReceived < responseSize); } + catch (SocketException) { } catch (IOException) { // Socket.Receive could throw, and that is fine From 18b53b43c1e2a0490e1622bca186e1afee7a7f98 Mon Sep 17 00:00:00 2001 From: Stephen Halter <halter73@gmail.com> Date: Thu, 5 Oct 2017 13:06:01 -0700 Subject: [PATCH 03/15] Moved OneToTenThreads test to Kestrel.Transport.Libuv.Tests --- .../ThreadCountTests.cs | 72 ------------------- .../LibuvTransportTests.cs | 50 +++++++++++++ 2 files changed, 50 insertions(+), 72 deletions(-) delete mode 100644 test/Kestrel.FunctionalTests/ThreadCountTests.cs diff --git a/test/Kestrel.FunctionalTests/ThreadCountTests.cs b/test/Kestrel.FunctionalTests/ThreadCountTests.cs deleted file mode 100644 index 1fce8183e..000000000 --- a/test/Kestrel.FunctionalTests/ThreadCountTests.cs +++ /dev/null @@ -1,72 +0,0 @@ -// 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.Collections.Generic; -using System.Net.Http; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Testing.xunit; -using Xunit; - -namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests -{ - public class ThreadCountTests - { - //[ConditionalTheory] - //[MemberData(nameof(OneToTen))] - //[OSSkipCondition(OperatingSystems.MacOSX, SkipReason = "Tests fail on OS X due to low file descriptor limit.")] - //public async Task OneToTenThreads(int threadCount) - //{ - // var hostBuilder = new WebHostBuilder() - // .UseKestrel() - // .UseLibuv(options => - // { - // options.ThreadCount = threadCount; - // }) - // .UseUrls("http://127.0.0.1:0/") - // .Configure(app => - // { - // app.Run(context => - // { - // return context.Response.WriteAsync("Hello World"); - // }); - // }); - - // using (var host = hostBuilder.Build()) - // { - // host.Start(); - - // using (var client = new HttpClient()) - // { - // // Send 20 requests just to make sure we don't get any failures - // var requestTasks = new List<Task<string>>(); - // for (int i = 0; i < 20; i++) - // { - // var requestTask = client.GetStringAsync($"http://127.0.0.1:{host.GetPort()}/"); - // requestTasks.Add(requestTask); - // } - - // foreach (var result in await Task.WhenAll(requestTasks)) - // { - // Assert.Equal("Hello World", result); - // } - // } - // } - //} - - public static TheoryData<int> OneToTen - { - get - { - var dataset = new TheoryData<int>(); - for (int i = 1; i <= 10; i++) - { - dataset.Add(i); - } - return dataset; - } - } - } -} diff --git a/test/Kestrel.Transport.Libuv.Tests/LibuvTransportTests.cs b/test/Kestrel.Transport.Libuv.Tests/LibuvTransportTests.cs index d3739ae07..f62a792b8 100644 --- a/test/Kestrel.Transport.Libuv.Tests/LibuvTransportTests.cs +++ b/test/Kestrel.Transport.Libuv.Tests/LibuvTransportTests.cs @@ -1,7 +1,10 @@ // 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.Collections.Generic; +using System.Linq; using System.Net; +using System.Net.Http; using System.Net.Sockets; using System.Text; using System.Threading.Tasks; @@ -10,6 +13,7 @@ using Microsoft.AspNetCore.Server.Kestrel.Core.Internal; using Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Tests.TestHelpers; using Microsoft.AspNetCore.Testing; +using Microsoft.AspNetCore.Testing.xunit; using Xunit; namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Tests @@ -25,6 +29,8 @@ public class LibuvTransportTests } }; + public static IEnumerable<object[]> OneToTen => Enumerable.Range(1, 10).Select(i => new object[] { i }); + [Fact] public async Task TransportCanBindAndStop() { @@ -80,5 +86,49 @@ public async Task ConnectionCanReadAndWrite(ListenOptions listenOptions) await transport.UnbindAsync(); await transport.StopAsync(); } + + [ConditionalTheory] + [MemberData(nameof(OneToTen))] + [OSSkipCondition(OperatingSystems.MacOSX, SkipReason = "Tests fail on OS X due to low file descriptor limit.")] + public async Task OneToTenThreads(int threadCount) + { + var listenOptions = new ListenOptions(new IPEndPoint(IPAddress.Loopback, 0)); + var serviceContext = new TestServiceContext(); + var testApplication = new DummyApplication(context => + { + return context.Response.WriteAsync("Hello World"); + }); + + listenOptions.UseHttpServer(listenOptions.ConnectionAdapters, serviceContext, testApplication, HttpProtocols.Http1); + + var transportContext = new TestLibuvTransportContext() + { + ConnectionHandler = new ConnectionHandler(serviceContext, listenOptions.Build()), + Options = new LibuvTransportOptions { ThreadCount = threadCount } + }; + + var transport = new LibuvTransport(transportContext, listenOptions); + + await transport.BindAsync(); + + using (var client = new HttpClient()) + { + // Send 20 requests just to make sure we don't get any failures + var requestTasks = new List<Task<string>>(); + for (int i = 0; i < 20; i++) + { + var requestTask = client.GetStringAsync($"http://127.0.0.1:{listenOptions.IPEndPoint.Port}/"); + requestTasks.Add(requestTask); + } + + foreach (var result in await Task.WhenAll(requestTasks)) + { + Assert.Equal("Hello World", result); + } + } + + await transport.UnbindAsync(); + await transport.StopAsync(); + } } } From 67f937c21d31585a85f7a0d6c9d5827800ed5f1c Mon Sep 17 00:00:00 2001 From: Stephen Halter <halter73@gmail.com> Date: Thu, 5 Oct 2017 15:02:19 -0700 Subject: [PATCH 04/15] Create separate Libuv and Sockets functional test projects --- .travis.yml | 2 +- KestrelHttpServer.sln | 55 +++++++++++++------ src/Kestrel.Core/Properties/AssemblyInfo.cs | 3 +- src/Kestrel.Https/Properties/AssemblyInfo.cs | 4 +- src/Kestrel/Kestrel.csproj | 2 + .../WebHostBuilderKestrelExtensions.cs | 8 ++- .../AddressRegistrationTests.cs | 22 ++++---- .../HttpProtocolSelectionTests.cs | 4 +- test/Kestrel.FunctionalTests/HttpsTests.cs | 14 ++--- .../LoggingConnectionAdapterTests.cs | 2 +- .../MaxRequestBufferSizeTests.cs | 2 +- test/Kestrel.FunctionalTests/RequestTests.cs | 16 +++--- test/Kestrel.FunctionalTests/ResponseTests.cs | 8 +-- .../TestHelpers/TestServer.cs | 2 +- ...el.Trasnport.Libuv.FunctionalTests.csproj} | 6 +- .../ListenHandleTests.cs | 0 .../TransportSelector.cs | 15 +++++ ...l.Trasnport.Sockets.FunctionalTests.csproj | 35 ++++++++++++ .../TransportSelector.cs | 15 +++++ .../SystemdActivation/Dockerfile | 0 .../SystemdActivation/docker-entrypoint.sh | 0 .../SystemdActivation/docker.sh | 0 22 files changed, 157 insertions(+), 58 deletions(-) rename test/{Kestrel.FunctionalTests/Kestrel.FunctionalTests.csproj => Kestrel.Transport.Libuv.FunctionalTests/Kestrel.Trasnport.Libuv.FunctionalTests.csproj} (76%) rename test/{Kestrel.FunctionalTests => Kestrel.Transport.Libuv.FunctionalTests}/ListenHandleTests.cs (100%) create mode 100644 test/Kestrel.Transport.Libuv.FunctionalTests/TransportSelector.cs create mode 100644 test/Kestrel.Transport.Sockets.FunctionalTests/Kestrel.Trasnport.Sockets.FunctionalTests.csproj create mode 100644 test/Kestrel.Transport.Sockets.FunctionalTests/TransportSelector.cs rename test/{Kestrel.FunctionalTests => }/SystemdActivation/Dockerfile (100%) rename test/{Kestrel.FunctionalTests => }/SystemdActivation/docker-entrypoint.sh (100%) rename test/{Kestrel.FunctionalTests => }/SystemdActivation/docker.sh (100%) mode change 100755 => 100644 diff --git a/.travis.yml b/.travis.yml index 73fb758d8..61ec7b534 100644 --- a/.travis.yml +++ b/.travis.yml @@ -28,4 +28,4 @@ before_install: - if test "$TRAVIS_OS_NAME" == "osx"; then brew update; brew install openssl; ln -s /usr/local/opt/openssl/lib/libcrypto.1.0.0.dylib /usr/local/lib/; ln -s /usr/local/opt/openssl/lib/libssl.1.0.0.dylib /usr/local/lib/; fi script: - ./build.sh - - if test "$TRAVIS_OS_NAME" != "osx"; then bash test/Kestrel.FunctionalTests/SystemdActivation/docker.sh; fi + - if test "$TRAVIS_OS_NAME" != "osx"; then bash test/SystemdActivation/docker.sh; fi diff --git a/KestrelHttpServer.sln b/KestrelHttpServer.sln index 0baa31521..1edf36770 100644 --- a/KestrelHttpServer.sln +++ b/KestrelHttpServer.sln @@ -1,6 +1,6 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 -VisualStudioVersion = 15.0.26814.0 +VisualStudioVersion = 15.0.26730.16 MinimumVisualStudioVersion = 15.0.26730.03 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{7972A5D6-3385-4127-9277-428506DD44FF}" ProjectSection(SolutionItems) = preProject @@ -68,8 +68,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CodeGenerator", "tools\Code EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Kestrel.Https", "src\Kestrel.Https\Kestrel.Https.csproj", "{5F64B3C3-0C2E-431A-B820-A81BBFC863DA}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Kestrel.FunctionalTests", "test\Kestrel.FunctionalTests\Kestrel.FunctionalTests.csproj", "{9559A5F1-080C-4909-B6CF-7E4B3DC55748}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Kestrel.Performance", "benchmarks\Kestrel.Performance\Kestrel.Performance.csproj", "{EBFE9719-A44B-4978-A71F-D5C254E7F35A}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "TestCertificates", "TestCertificates", "{2822C132-BFFB-4D53-AC5B-E7E47DD81A6E}" @@ -112,6 +110,17 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "build", "build", "{C2910A13 build\repo.targets = build\repo.targets EndProjectSection EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SystemdActivation", "SystemdActivation", "{B7B0EA74-528F-46B8-9BC4-909D9A67C194}" + ProjectSection(SolutionItems) = preProject + test\SystemdActivation\docker-entrypoint.sh = test\SystemdActivation\docker-entrypoint.sh + test\SystemdActivation\docker.sh = test\SystemdActivation\docker.sh + test\SystemdActivation\Dockerfile = test\SystemdActivation\Dockerfile + EndProjectSection +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Kestrel.Trasnport.Libuv.FunctionalTests", "test\Kestrel.Transport.Libuv.FunctionalTests\Kestrel.Trasnport.Libuv.FunctionalTests.csproj", "{74032D79-8EA7-4483-BD82-C38370420FFF}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Kestrel.Trasnport.Sockets.FunctionalTests", "test\Kestrel.Transport.Sockets.FunctionalTests\Kestrel.Trasnport.Sockets.FunctionalTests.csproj", "{9C7B6B5F-088A-436E-834B-6373EA36DEEE}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -194,18 +203,6 @@ Global {5F64B3C3-0C2E-431A-B820-A81BBFC863DA}.Release|x64.Build.0 = Release|Any CPU {5F64B3C3-0C2E-431A-B820-A81BBFC863DA}.Release|x86.ActiveCfg = Release|Any CPU {5F64B3C3-0C2E-431A-B820-A81BBFC863DA}.Release|x86.Build.0 = Release|Any CPU - {9559A5F1-080C-4909-B6CF-7E4B3DC55748}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {9559A5F1-080C-4909-B6CF-7E4B3DC55748}.Debug|Any CPU.Build.0 = Debug|Any CPU - {9559A5F1-080C-4909-B6CF-7E4B3DC55748}.Debug|x64.ActiveCfg = Debug|Any CPU - {9559A5F1-080C-4909-B6CF-7E4B3DC55748}.Debug|x64.Build.0 = Debug|Any CPU - {9559A5F1-080C-4909-B6CF-7E4B3DC55748}.Debug|x86.ActiveCfg = Debug|Any CPU - {9559A5F1-080C-4909-B6CF-7E4B3DC55748}.Debug|x86.Build.0 = Debug|Any CPU - {9559A5F1-080C-4909-B6CF-7E4B3DC55748}.Release|Any CPU.ActiveCfg = Release|Any CPU - {9559A5F1-080C-4909-B6CF-7E4B3DC55748}.Release|Any CPU.Build.0 = Release|Any CPU - {9559A5F1-080C-4909-B6CF-7E4B3DC55748}.Release|x64.ActiveCfg = Release|Any CPU - {9559A5F1-080C-4909-B6CF-7E4B3DC55748}.Release|x64.Build.0 = Release|Any CPU - {9559A5F1-080C-4909-B6CF-7E4B3DC55748}.Release|x86.ActiveCfg = Release|Any CPU - {9559A5F1-080C-4909-B6CF-7E4B3DC55748}.Release|x86.Build.0 = Release|Any CPU {EBFE9719-A44B-4978-A71F-D5C254E7F35A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {EBFE9719-A44B-4978-A71F-D5C254E7F35A}.Debug|Any CPU.Build.0 = Debug|Any CPU {EBFE9719-A44B-4978-A71F-D5C254E7F35A}.Debug|x64.ActiveCfg = Debug|Any CPU @@ -314,6 +311,30 @@ Global {924AE57C-1EBA-4A1D-A039-8C100B7507A5}.Release|x64.Build.0 = Release|Any CPU {924AE57C-1EBA-4A1D-A039-8C100B7507A5}.Release|x86.ActiveCfg = Release|Any CPU {924AE57C-1EBA-4A1D-A039-8C100B7507A5}.Release|x86.Build.0 = Release|Any CPU + {74032D79-8EA7-4483-BD82-C38370420FFF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {74032D79-8EA7-4483-BD82-C38370420FFF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {74032D79-8EA7-4483-BD82-C38370420FFF}.Debug|x64.ActiveCfg = Debug|Any CPU + {74032D79-8EA7-4483-BD82-C38370420FFF}.Debug|x64.Build.0 = Debug|Any CPU + {74032D79-8EA7-4483-BD82-C38370420FFF}.Debug|x86.ActiveCfg = Debug|Any CPU + {74032D79-8EA7-4483-BD82-C38370420FFF}.Debug|x86.Build.0 = Debug|Any CPU + {74032D79-8EA7-4483-BD82-C38370420FFF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {74032D79-8EA7-4483-BD82-C38370420FFF}.Release|Any CPU.Build.0 = Release|Any CPU + {74032D79-8EA7-4483-BD82-C38370420FFF}.Release|x64.ActiveCfg = Release|Any CPU + {74032D79-8EA7-4483-BD82-C38370420FFF}.Release|x64.Build.0 = Release|Any CPU + {74032D79-8EA7-4483-BD82-C38370420FFF}.Release|x86.ActiveCfg = Release|Any CPU + {74032D79-8EA7-4483-BD82-C38370420FFF}.Release|x86.Build.0 = Release|Any CPU + {9C7B6B5F-088A-436E-834B-6373EA36DEEE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9C7B6B5F-088A-436E-834B-6373EA36DEEE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9C7B6B5F-088A-436E-834B-6373EA36DEEE}.Debug|x64.ActiveCfg = Debug|Any CPU + {9C7B6B5F-088A-436E-834B-6373EA36DEEE}.Debug|x64.Build.0 = Debug|Any CPU + {9C7B6B5F-088A-436E-834B-6373EA36DEEE}.Debug|x86.ActiveCfg = Debug|Any CPU + {9C7B6B5F-088A-436E-834B-6373EA36DEEE}.Debug|x86.Build.0 = Debug|Any CPU + {9C7B6B5F-088A-436E-834B-6373EA36DEEE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9C7B6B5F-088A-436E-834B-6373EA36DEEE}.Release|Any CPU.Build.0 = Release|Any CPU + {9C7B6B5F-088A-436E-834B-6373EA36DEEE}.Release|x64.ActiveCfg = Release|Any CPU + {9C7B6B5F-088A-436E-834B-6373EA36DEEE}.Release|x64.Build.0 = Release|Any CPU + {9C7B6B5F-088A-436E-834B-6373EA36DEEE}.Release|x86.ActiveCfg = Release|Any CPU + {9C7B6B5F-088A-436E-834B-6373EA36DEEE}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -325,7 +346,6 @@ Global {B35D4D31-E74C-4646-8A11-7A7A40F0021E} = {8A3D00B8-1CCF-4BE6-A060-11104CE2D9CE} {BD2D4D29-1BD9-40D0-BB31-337D5416B63C} = {327F7880-D9AF-46BD-B45C-3B7E34A01DFD} {5F64B3C3-0C2E-431A-B820-A81BBFC863DA} = {2D5D5227-4DBD-499A-96B1-76A36B03B750} - {9559A5F1-080C-4909-B6CF-7E4B3DC55748} = {D3273454-EA07-41D2-BF0B-FCC3675C2483} {EBFE9719-A44B-4978-A71F-D5C254E7F35A} = {A95C3BE1-B850-4265-97A0-777ADCCD437F} {2822C132-BFFB-4D53-AC5B-E7E47DD81A6E} = {0EF2ACDF-012F-4472-A13A-4272419E2903} {A76B8C8C-0DC5-4DD3-9B1F-02E51A0915F4} = {2D5D5227-4DBD-499A-96B1-76A36B03B750} @@ -336,6 +356,9 @@ Global {4F1C30F8-CCAA-48D7-9DF6-2A84021F5BCC} = {D3273454-EA07-41D2-BF0B-FCC3675C2483} {6956CF5C-3163-4398-8628-4ECA569245B5} = {2D5D5227-4DBD-499A-96B1-76A36B03B750} {924AE57C-1EBA-4A1D-A039-8C100B7507A5} = {2D5D5227-4DBD-499A-96B1-76A36B03B750} + {B7B0EA74-528F-46B8-9BC4-909D9A67C194} = {D3273454-EA07-41D2-BF0B-FCC3675C2483} + {74032D79-8EA7-4483-BD82-C38370420FFF} = {D3273454-EA07-41D2-BF0B-FCC3675C2483} + {9C7B6B5F-088A-436E-834B-6373EA36DEEE} = {D3273454-EA07-41D2-BF0B-FCC3675C2483} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {2D10D020-6770-47CA-BB8D-2C23FE3AE071} diff --git a/src/Kestrel.Core/Properties/AssemblyInfo.cs b/src/Kestrel.Core/Properties/AssemblyInfo.cs index bd1d32689..e2d152d0d 100644 --- a/src/Kestrel.Core/Properties/AssemblyInfo.cs +++ b/src/Kestrel.Core/Properties/AssemblyInfo.cs @@ -3,7 +3,8 @@ using System.Runtime.CompilerServices; -[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Server.Kestrel.FunctionalTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] +[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.FunctionalTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] +[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets.FunctionalTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] [assembly: InternalsVisibleTo("Microsoft.AspNetCore.Server.Kestrel.Core.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] [assembly: InternalsVisibleTo("Kestrel.Performance, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] [assembly: InternalsVisibleTo("Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] diff --git a/src/Kestrel.Https/Properties/AssemblyInfo.cs b/src/Kestrel.Https/Properties/AssemblyInfo.cs index 3bb5150c9..65c2045e2 100644 --- a/src/Kestrel.Https/Properties/AssemblyInfo.cs +++ b/src/Kestrel.Https/Properties/AssemblyInfo.cs @@ -3,4 +3,6 @@ using System.Runtime.CompilerServices; -[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Server.Kestrel.FunctionalTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] +[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.FunctionalTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] +[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets.FunctionalTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] + diff --git a/src/Kestrel/Kestrel.csproj b/src/Kestrel/Kestrel.csproj index 0cc57be49..8cacb89c6 100644 --- a/src/Kestrel/Kestrel.csproj +++ b/src/Kestrel/Kestrel.csproj @@ -16,6 +16,8 @@ <ItemGroup> <ProjectReference Include="..\Kestrel.Core\Kestrel.Core.csproj" /> + <!-- Even though the Libuv transport is no longer used by default, it's remains for back-compat --> + <ProjectReference Include="..\Kestrel.Transport.Sockets\Kestrel.Transport.Libuv.csproj" /> <ProjectReference Include="..\Kestrel.Transport.Sockets\Kestrel.Transport.Sockets.csproj" /> </ItemGroup> diff --git a/src/Kestrel/WebHostBuilderKestrelExtensions.cs b/src/Kestrel/WebHostBuilderKestrelExtensions.cs index 6c28b16a2..5ed947e96 100644 --- a/src/Kestrel/WebHostBuilderKestrelExtensions.cs +++ b/src/Kestrel/WebHostBuilderKestrelExtensions.cs @@ -5,7 +5,10 @@ using Microsoft.AspNetCore.Hosting.Server; using Microsoft.AspNetCore.Server.Kestrel.Core; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal; +using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal; +using Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Options; namespace Microsoft.AspNetCore.Hosting @@ -23,10 +26,11 @@ public static class WebHostBuilderKestrelExtensions /// </returns> public static IWebHostBuilder UseKestrel(this IWebHostBuilder hostBuilder) { - hostBuilder.UseSockets(); - return hostBuilder.ConfigureServices(services => { + // Don't override an already-configured transport + services.TryAddSingleton<ITransportFactory, SocketTransportFactory>(); + services.AddTransient<IConfigureOptions<KestrelServerOptions>, KestrelServerOptionsSetup>(); services.AddSingleton<IServer, KestrelServer>(); }); diff --git a/test/Kestrel.FunctionalTests/AddressRegistrationTests.cs b/test/Kestrel.FunctionalTests/AddressRegistrationTests.cs index b9f6e4303..e2e7bea98 100644 --- a/test/Kestrel.FunctionalTests/AddressRegistrationTests.cs +++ b/test/Kestrel.FunctionalTests/AddressRegistrationTests.cs @@ -163,7 +163,7 @@ public async Task RegisterAddresses_IPv6LocalhostStaticPort_Success() private async Task RegisterAddresses_Success(string addressInput, string[] testUrls, int testPort = 0) { - var hostBuilder = new WebHostBuilder() + var hostBuilder = TransportSelector.GetWebHostBuilder() .UseKestrel() .ConfigureLogging(_configureLoggingDelegate) .UseUrls(addressInput) @@ -223,7 +223,7 @@ private Task RegisterAddresses_StaticPort_Success(string addressInput, string te private async Task RegisterIPEndPoint_Success(IPEndPoint endPoint, string testUrl, int testPort = 0) { - var hostBuilder = new WebHostBuilder() + var hostBuilder = TransportSelector.GetWebHostBuilder() .ConfigureLogging(_configureLoggingDelegate) .UseKestrel(options => { @@ -304,7 +304,7 @@ private async Task RegisterDefaultServerAddresses_Success(IEnumerable<string> ad { var testLogger = new TestApplicationErrorLogger(); - var hostBuilder = new WebHostBuilder() + var hostBuilder = TransportSelector.GetWebHostBuilder() .ConfigureLogging(_configureLoggingDelegate) .UseKestrel() .ConfigureLogging(builder => builder @@ -336,7 +336,7 @@ public void ThrowsWhenBindingToIPv4AddressInUse() socket.Bind(new IPEndPoint(IPAddress.Loopback, 0)); var port = ((IPEndPoint)socket.LocalEndPoint).Port; - var hostBuilder = new WebHostBuilder() + var hostBuilder = TransportSelector.GetWebHostBuilder() .ConfigureLogging(_configureLoggingDelegate) .UseKestrel() .UseUrls($"http://127.0.0.1:{port}") @@ -359,7 +359,7 @@ public void ThrowsWhenBindingToIPv6AddressInUse() socket.Bind(new IPEndPoint(IPAddress.IPv6Loopback, 0)); var port = ((IPEndPoint)socket.LocalEndPoint).Port; - var hostBuilder = new WebHostBuilder() + var hostBuilder = TransportSelector.GetWebHostBuilder() .ConfigureLogging(_configureLoggingDelegate) .UseKestrel() .UseUrls($"http://[::1]:{port}") @@ -378,7 +378,7 @@ public async Task OverrideDirectConfigurationWithIServerAddressesFeature_Succeed { var useUrlsAddress = $"http://127.0.0.1:0"; var testLogger = new TestApplicationErrorLogger(); - var hostBuilder = new WebHostBuilder() + var hostBuilder = TransportSelector.GetWebHostBuilder() .UseKestrel(options => { options.Listen(new IPEndPoint(IPAddress.Loopback, 0), listenOptions => @@ -417,7 +417,7 @@ public async Task DoesNotOverrideDirectConfigurationWithIServerAddressesFeature_ { var useUrlsAddress = $"http://127.0.0.1:0"; var testLogger = new TestApplicationErrorLogger(); - var hostBuilder = new WebHostBuilder() + var hostBuilder = TransportSelector.GetWebHostBuilder() .ConfigureLogging(_configureLoggingDelegate) .UseKestrel(options => { @@ -455,7 +455,7 @@ public async Task DoesNotOverrideDirectConfigurationWithIServerAddressesFeature_ [Fact] public async Task DoesNotOverrideDirectConfigurationWithIServerAddressesFeature_IfAddressesEmpty() { - var hostBuilder = new WebHostBuilder() + var hostBuilder = TransportSelector.GetWebHostBuilder() .ConfigureLogging(_configureLoggingDelegate) .UseKestrel(options => { @@ -499,7 +499,7 @@ public void ThrowsWhenBindingLocalhostToIPv6AddressInUse() [Fact] public void ThrowsWhenBindingLocalhostToDynamicPort() { - var hostBuilder = new WebHostBuilder() + var hostBuilder = TransportSelector.GetWebHostBuilder() .UseKestrel() .UseUrls("http://localhost:0") .Configure(ConfigureEchoAddress); @@ -515,7 +515,7 @@ public void ThrowsWhenBindingLocalhostToDynamicPort() [InlineData("ftp://localhost")] public void ThrowsForUnsupportedAddressFromHosting(string addr) { - var hostBuilder = new WebHostBuilder() + var hostBuilder = TransportSelector.GetWebHostBuilder() .UseKestrel() .UseUrls(addr) .Configure(ConfigureEchoAddress); @@ -533,7 +533,7 @@ private void ThrowsWhenBindingLocalhostToAddressInUse(AddressFamily addressFamil socket.Bind(new IPEndPoint(address, 0)); var port = ((IPEndPoint)socket.LocalEndPoint).Port; - var hostBuilder = new WebHostBuilder() + var hostBuilder = TransportSelector.GetWebHostBuilder() .ConfigureLogging(_configureLoggingDelegate) .UseKestrel() .UseUrls($"http://localhost:{port}") diff --git a/test/Kestrel.FunctionalTests/HttpProtocolSelectionTests.cs b/test/Kestrel.FunctionalTests/HttpProtocolSelectionTests.cs index 7b665d5ef..c2dd742c1 100644 --- a/test/Kestrel.FunctionalTests/HttpProtocolSelectionTests.cs +++ b/test/Kestrel.FunctionalTests/HttpProtocolSelectionTests.cs @@ -45,7 +45,7 @@ public Task Server_Http2Only_Cleartext_Success() private async Task TestSuccess(HttpProtocols serverProtocols, string request, string expectedResponse) { - var builder = new WebHostBuilder() + var builder = TransportSelector.GetWebHostBuilder() .UseKestrel(options => { options.Listen(IPAddress.Loopback, 0, listenOptions => listenOptions.Protocols = serverProtocols); @@ -73,7 +73,7 @@ private async Task TestError<TException>(HttpProtocols serverProtocols, string e .Setup(provider => provider.CreateLogger(It.IsAny<string>())) .Returns(logger); - var builder = new WebHostBuilder() + var builder = TransportSelector.GetWebHostBuilder() .ConfigureLogging(loggingBuilder => loggingBuilder.AddProvider(loggerProvider.Object)) .UseKestrel(options => options.Listen(IPAddress.Loopback, 0, listenOptions => listenOptions.Protocols = serverProtocols)) .Configure(app => app.Run(context => Task.CompletedTask)); diff --git a/test/Kestrel.FunctionalTests/HttpsTests.cs b/test/Kestrel.FunctionalTests/HttpsTests.cs index 604df5943..8bc8fb7f6 100644 --- a/test/Kestrel.FunctionalTests/HttpsTests.cs +++ b/test/Kestrel.FunctionalTests/HttpsTests.cs @@ -31,7 +31,7 @@ public async Task EmptyRequestLoggedAsInformation() { var loggerProvider = new HandshakeErrorLoggerProvider(); - var hostBuilder = new WebHostBuilder() + var hostBuilder = TransportSelector.GetWebHostBuilder() .UseKestrel(options => { options.Listen(new IPEndPoint(IPAddress.Loopback, 0), listenOptions => @@ -65,7 +65,7 @@ public async Task ClientHandshakeFailureLoggedAsInformation() { var loggerProvider = new HandshakeErrorLoggerProvider(); - var hostBuilder = new WebHostBuilder() + var hostBuilder = TransportSelector.GetWebHostBuilder() .UseKestrel(options => { options.Listen(new IPEndPoint(IPAddress.Loopback, 0), listenOptions => @@ -101,7 +101,7 @@ public async Task ClientHandshakeFailureLoggedAsInformation() public async Task DoesNotThrowObjectDisposedExceptionOnConnectionAbort() { var loggerProvider = new HandshakeErrorLoggerProvider(); - var hostBuilder = new WebHostBuilder() + var hostBuilder = TransportSelector.GetWebHostBuilder() .UseKestrel(options => { options.Listen(new IPEndPoint(IPAddress.Loopback, 0), listenOptions => @@ -153,7 +153,7 @@ public async Task DoesNotThrowObjectDisposedExceptionFromWriteAsyncAfterConnecti { var tcs = new TaskCompletionSource<object>(); var loggerProvider = new HandshakeErrorLoggerProvider(); - var hostBuilder = new WebHostBuilder() + var hostBuilder = TransportSelector.GetWebHostBuilder() .UseKestrel(options => { options.Listen(new IPEndPoint(IPAddress.Loopback, 0), listenOptions => @@ -202,7 +202,7 @@ await sslStream.AuthenticateAsClientAsync("127.0.0.1", clientCertificates: null, public async Task DoesNotThrowObjectDisposedExceptionOnEmptyConnection() { var loggerProvider = new HandshakeErrorLoggerProvider(); - var hostBuilder = new WebHostBuilder() + var hostBuilder = TransportSelector.GetWebHostBuilder() .UseKestrel(options => { options.Listen(new IPEndPoint(IPAddress.Loopback, 0), listenOptions => @@ -236,7 +236,7 @@ public void ConnectionFilterDoesNotLeakBlock() { var loggerProvider = new HandshakeErrorLoggerProvider(); - var hostBuilder = new WebHostBuilder() + var hostBuilder = TransportSelector.GetWebHostBuilder() .UseKestrel(options => { options.Listen(new IPEndPoint(IPAddress.Loopback, 0), listenOptions => @@ -265,7 +265,7 @@ public void ConnectionFilterDoesNotLeakBlock() public async Task HandshakeTimesOutAndIsLoggedAsInformation() { var loggerProvider = new HandshakeErrorLoggerProvider(); - var hostBuilder = new WebHostBuilder() + var hostBuilder = TransportSelector.GetWebHostBuilder() .UseKestrel(options => { options.Listen(new IPEndPoint(IPAddress.Loopback, 0), listenOptions => diff --git a/test/Kestrel.FunctionalTests/LoggingConnectionAdapterTests.cs b/test/Kestrel.FunctionalTests/LoggingConnectionAdapterTests.cs index 24e72aa33..12f7e604e 100644 --- a/test/Kestrel.FunctionalTests/LoggingConnectionAdapterTests.cs +++ b/test/Kestrel.FunctionalTests/LoggingConnectionAdapterTests.cs @@ -26,7 +26,7 @@ public LoggingConnectionAdapterTests(ITestOutputHelper output) [Fact] public async Task LoggingConnectionAdapterCanBeAddedBeforeAndAfterHttpsAdapter() { - var host = new WebHostBuilder() + var host = TransportSelector.GetWebHostBuilder() .ConfigureLogging(builder => { builder.SetMinimumLevel(LogLevel.Trace); diff --git a/test/Kestrel.FunctionalTests/MaxRequestBufferSizeTests.cs b/test/Kestrel.FunctionalTests/MaxRequestBufferSizeTests.cs index 9cda0bbde..79cdee529 100644 --- a/test/Kestrel.FunctionalTests/MaxRequestBufferSizeTests.cs +++ b/test/Kestrel.FunctionalTests/MaxRequestBufferSizeTests.cs @@ -258,7 +258,7 @@ private IWebHost StartWebHost(long? maxRequestBufferSize, TaskCompletionSource<object> startReadingRequestBody, TaskCompletionSource<object> clientFinishedSendingRequestBody) { - var host = new WebHostBuilder() + var host = TransportSelector.GetWebHostBuilder() .ConfigureLogging(_configureLoggingDelegate) .UseKestrel(options => { diff --git a/test/Kestrel.FunctionalTests/RequestTests.cs b/test/Kestrel.FunctionalTests/RequestTests.cs index e0563d398..46933f427 100644 --- a/test/Kestrel.FunctionalTests/RequestTests.cs +++ b/test/Kestrel.FunctionalTests/RequestTests.cs @@ -69,7 +69,7 @@ public void LargeUpload(long contentLength, bool checkBytes) Assert.True(contentLength % bufferLength == 0, $"{nameof(contentLength)} sent must be evenly divisible by {bufferLength}."); Assert.True(bufferLength % 256 == 0, $"{nameof(bufferLength)} must be evenly divisible by 256"); - var builder = new WebHostBuilder() + var builder = TransportSelector.GetWebHostBuilder() .UseKestrel(options => { options.Limits.MaxRequestBodySize = contentLength; @@ -155,7 +155,7 @@ public Task RemoteIPv6Address() [Fact] public async Task DoesNotHangOnConnectionCloseRequest() { - var builder = new WebHostBuilder() + var builder = TransportSelector.GetWebHostBuilder() .UseKestrel() .UseUrls("http://127.0.0.1:0") .Configure(app => @@ -185,7 +185,7 @@ public async Task StreamsAreNotPersistedAcrossRequests() var requestBodyPersisted = false; var responseBodyPersisted = false; - var builder = new WebHostBuilder() + var builder = TransportSelector.GetWebHostBuilder() .UseKestrel() .UseUrls("http://127.0.0.1:0") .Configure(app => @@ -228,7 +228,7 @@ public async Task StreamsAreNotPersistedAcrossRequests() public void CanUpgradeRequestWithConnectionKeepAliveUpgradeHeader() { var dataRead = false; - var builder = new WebHostBuilder() + var builder = TransportSelector.GetWebHostBuilder() .UseKestrel() .UseUrls("http://127.0.0.1:0") .Configure(app => @@ -470,7 +470,7 @@ public async Task ThrowsOnReadAfterConnectionError() var appDone = new SemaphoreSlim(0); var expectedExceptionThrown = false; - var builder = new WebHostBuilder() + var builder = TransportSelector.GetWebHostBuilder() .UseKestrel() .UseUrls("http://127.0.0.1:0") .Configure(app => app.Run(async context => @@ -514,7 +514,7 @@ public async Task RequestAbortedTokenFiredOnClientFIN() { var appStarted = new SemaphoreSlim(0); var requestAborted = new SemaphoreSlim(0); - var builder = new WebHostBuilder() + var builder = TransportSelector.GetWebHostBuilder() .UseKestrel() .UseUrls("http://127.0.0.1:0") .Configure(app => app.Run(async context => @@ -544,7 +544,7 @@ public async Task RequestAbortedTokenFiredOnClientFIN() [Fact] public void AbortingTheConnectionSendsFIN() { - var builder = new WebHostBuilder() + var builder = TransportSelector.GetWebHostBuilder() .UseKestrel() .UseUrls("http://127.0.0.1:0") .Configure(app => app.Run(context => @@ -1623,7 +1623,7 @@ await connection.Receive( private async Task TestRemoteIPAddress(string registerAddress, string requestAddress, string expectAddress) { - var builder = new WebHostBuilder() + var builder = TransportSelector.GetWebHostBuilder() .UseKestrel() .UseUrls($"http://{registerAddress}:0") .Configure(app => diff --git a/test/Kestrel.FunctionalTests/ResponseTests.cs b/test/Kestrel.FunctionalTests/ResponseTests.cs index 7dfa240da..7879e4d72 100644 --- a/test/Kestrel.FunctionalTests/ResponseTests.cs +++ b/test/Kestrel.FunctionalTests/ResponseTests.cs @@ -49,7 +49,7 @@ public class ResponseTests [Fact] public async Task LargeDownload() { - var hostBuilder = new WebHostBuilder() + var hostBuilder = TransportSelector.GetWebHostBuilder() .UseKestrel() .UseUrls("http://127.0.0.1:0/") .Configure(app => @@ -101,7 +101,7 @@ public async Task LargeDownload() [Theory, MemberData(nameof(NullHeaderData))] public async Task IgnoreNullHeaderValues(string headerName, StringValues headerValue, string expectedValue) { - var hostBuilder = new WebHostBuilder() + var hostBuilder = TransportSelector.GetWebHostBuilder() .UseKestrel() .UseUrls("http://127.0.0.1:0/") .Configure(app => @@ -144,7 +144,7 @@ public async Task OnCompleteCalledEvenWhenOnStartingNotCalled() var onStartingCalled = false; var onCompletedCalled = false; - var hostBuilder = new WebHostBuilder() + var hostBuilder = TransportSelector.GetWebHostBuilder() .UseKestrel() .UseUrls("http://127.0.0.1:0/") .Configure(app => @@ -179,7 +179,7 @@ public async Task OnStartingThrowsWhenSetAfterResponseHasAlreadyStarted() { InvalidOperationException ex = null; - var hostBuilder = new WebHostBuilder() + var hostBuilder = TransportSelector.GetWebHostBuilder() .UseKestrel() .UseUrls("http://127.0.0.1:0/") .Configure(app => diff --git a/test/Kestrel.FunctionalTests/TestHelpers/TestServer.cs b/test/Kestrel.FunctionalTests/TestHelpers/TestServer.cs index 5e36ddf4a..f0972bb4f 100644 --- a/test/Kestrel.FunctionalTests/TestHelpers/TestServer.cs +++ b/test/Kestrel.FunctionalTests/TestHelpers/TestServer.cs @@ -48,7 +48,7 @@ public TestServer(RequestDelegate app, TestServiceContext context, ListenOptions _listenOptions = listenOptions; Context = context; - _host = new WebHostBuilder() + _host = TransportSelector.GetWebHostBuilder() .UseKestrel(o => { o.ListenOptions.Add(_listenOptions); diff --git a/test/Kestrel.FunctionalTests/Kestrel.FunctionalTests.csproj b/test/Kestrel.Transport.Libuv.FunctionalTests/Kestrel.Trasnport.Libuv.FunctionalTests.csproj similarity index 76% rename from test/Kestrel.FunctionalTests/Kestrel.FunctionalTests.csproj rename to test/Kestrel.Transport.Libuv.FunctionalTests/Kestrel.Trasnport.Libuv.FunctionalTests.csproj index 3ed0a438b..b4c4d93b6 100644 --- a/test/Kestrel.FunctionalTests/Kestrel.FunctionalTests.csproj +++ b/test/Kestrel.Transport.Libuv.FunctionalTests/Kestrel.Trasnport.Libuv.FunctionalTests.csproj @@ -1,8 +1,8 @@ <Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> - <AssemblyName>Microsoft.AspNetCore.Server.Kestrel.FunctionalTests</AssemblyName> - <RootNamespace>Microsoft.AspNetCore.Server.Kestrel.FunctionalTests</RootNamespace> + <AssemblyName>Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.FunctionalTests</AssemblyName> + <RootNamespace>Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.FunctionalTests</RootNamespace> <TargetFrameworks>netcoreapp2.0;net461</TargetFrameworks> <TargetFrameworks Condition=" '$(OS)' != 'Windows_NT' ">netcoreapp2.0</TargetFrameworks> <DefineConstants Condition="$([MSBuild]::IsOSPlatform('OSX'))">$(DefineConstants);MACOS</DefineConstants> @@ -11,6 +11,7 @@ <ItemGroup> <Compile Include="..\shared\**\*.cs" /> + <Compile Include="..\Kestrel.FunctionalTests\**\*.cs" /> <Content Include="..\shared\TestCertificates\*.pfx" CopyToOutputDirectory="PreserveNewest" /> </ItemGroup> @@ -20,6 +21,7 @@ <ItemGroup> <ProjectReference Include="..\..\src\Kestrel\Kestrel.csproj" /> + <ProjectReference Include="..\..\src\Kestrel.Transport.Libuv\Kestrel.Transport.Libuv.csproj" /> <ProjectReference Include="..\..\src\Kestrel.Https\Kestrel.Https.csproj" /> </ItemGroup> diff --git a/test/Kestrel.FunctionalTests/ListenHandleTests.cs b/test/Kestrel.Transport.Libuv.FunctionalTests/ListenHandleTests.cs similarity index 100% rename from test/Kestrel.FunctionalTests/ListenHandleTests.cs rename to test/Kestrel.Transport.Libuv.FunctionalTests/ListenHandleTests.cs diff --git a/test/Kestrel.Transport.Libuv.FunctionalTests/TransportSelector.cs b/test/Kestrel.Transport.Libuv.FunctionalTests/TransportSelector.cs new file mode 100644 index 000000000..cba3395d1 --- /dev/null +++ b/test/Kestrel.Transport.Libuv.FunctionalTests/TransportSelector.cs @@ -0,0 +1,15 @@ +// 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 Microsoft.AspNetCore.Hosting; + +namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests +{ + public static class TransportSelector + { + public static IWebHostBuilder GetWebHostBuilder() + { + return new WebHostBuilder().UseLibuv(); + } + } +} diff --git a/test/Kestrel.Transport.Sockets.FunctionalTests/Kestrel.Trasnport.Sockets.FunctionalTests.csproj b/test/Kestrel.Transport.Sockets.FunctionalTests/Kestrel.Trasnport.Sockets.FunctionalTests.csproj new file mode 100644 index 000000000..731a6c363 --- /dev/null +++ b/test/Kestrel.Transport.Sockets.FunctionalTests/Kestrel.Trasnport.Sockets.FunctionalTests.csproj @@ -0,0 +1,35 @@ +<Project Sdk="Microsoft.NET.Sdk"> + + <PropertyGroup> + <AssemblyName>Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets.FunctionalTests</AssemblyName> + <RootNamespace>Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets.FunctionalTests</RootNamespace> + <TargetFrameworks>netcoreapp2.0;net461</TargetFrameworks> + <TargetFrameworks Condition=" '$(OS)' != 'Windows_NT' ">netcoreapp2.0</TargetFrameworks> + <DefineConstants Condition="$([MSBuild]::IsOSPlatform('OSX'))">$(DefineConstants);MACOS</DefineConstants> + <ServerGarbageCollection>true</ServerGarbageCollection> + </PropertyGroup> + + <ItemGroup> + <Compile Include="..\shared\**\*.cs" /> + <Compile Include="..\Kestrel.FunctionalTests\**\*.cs" /> + <Content Include="..\shared\TestCertificates\*.pfx" CopyToOutputDirectory="PreserveNewest" /> + </ItemGroup> + + <ItemGroup Condition="'$(TargetFramework)' == 'netcoreapp2.0'"> + <ProjectReference Include="..\..\tools\CodeGenerator\CodeGenerator.csproj" /> + </ItemGroup> + + <ItemGroup> + <ProjectReference Include="..\..\src\Kestrel\Kestrel.csproj" /> + <ProjectReference Include="..\..\src\Kestrel.Transport.Sockets\Kestrel.Transport.Sockets.csproj" /> + <ProjectReference Include="..\..\src\Kestrel.Https\Kestrel.Https.csproj" /> + </ItemGroup> + + <ItemGroup> + <PackageReference Include="Microsoft.AspNetCore.Http.Abstractions" /> + <PackageReference Include="Microsoft.AspNetCore.Testing" /> + <PackageReference Include="Microsoft.Extensions.Logging.Testing" /> + <PackageReference Include="Newtonsoft.Json" /> + </ItemGroup> + +</Project> diff --git a/test/Kestrel.Transport.Sockets.FunctionalTests/TransportSelector.cs b/test/Kestrel.Transport.Sockets.FunctionalTests/TransportSelector.cs new file mode 100644 index 000000000..1a433cc9e --- /dev/null +++ b/test/Kestrel.Transport.Sockets.FunctionalTests/TransportSelector.cs @@ -0,0 +1,15 @@ +// 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 Microsoft.AspNetCore.Hosting; + +namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests +{ + public static class TransportSelector + { + public static IWebHostBuilder GetWebHostBuilder() + { + return new WebHostBuilder().UseSockets(); + } + } +} diff --git a/test/Kestrel.FunctionalTests/SystemdActivation/Dockerfile b/test/SystemdActivation/Dockerfile similarity index 100% rename from test/Kestrel.FunctionalTests/SystemdActivation/Dockerfile rename to test/SystemdActivation/Dockerfile diff --git a/test/Kestrel.FunctionalTests/SystemdActivation/docker-entrypoint.sh b/test/SystemdActivation/docker-entrypoint.sh similarity index 100% rename from test/Kestrel.FunctionalTests/SystemdActivation/docker-entrypoint.sh rename to test/SystemdActivation/docker-entrypoint.sh diff --git a/test/Kestrel.FunctionalTests/SystemdActivation/docker.sh b/test/SystemdActivation/docker.sh old mode 100755 new mode 100644 similarity index 100% rename from test/Kestrel.FunctionalTests/SystemdActivation/docker.sh rename to test/SystemdActivation/docker.sh From 49a0be5dfde8d700ebbd2857c8cef334a704f8e3 Mon Sep 17 00:00:00 2001 From: Stephen Halter <halter73@gmail.com> Date: Thu, 5 Oct 2017 16:30:26 -0700 Subject: [PATCH 05/15] Update socket transport baseline - No RTM version of this package has been shipped. --- src/Kestrel.Transport.Sockets/baseline.netcore.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Kestrel.Transport.Sockets/baseline.netcore.json b/src/Kestrel.Transport.Sockets/baseline.netcore.json index e614b8097..bd469c578 100644 --- a/src/Kestrel.Transport.Sockets/baseline.netcore.json +++ b/src/Kestrel.Transport.Sockets/baseline.netcore.json @@ -83,6 +83,10 @@ { "Name": "options", "Type": "Microsoft.Extensions.Options.IOptions<Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets.SocketTransportOptions>" + }, + { + "Name": "loggerFactory", + "Type": "Microsoft.Extensions.Logging.ILoggerFactory" } ], "Visibility": "Public", From 272e45098f5f1a82c4b2fe68304ca62528d2ebdd Mon Sep 17 00:00:00 2001 From: Stephen Halter <halter73@gmail.com> Date: Thu, 5 Oct 2017 17:15:05 -0700 Subject: [PATCH 06/15] Kill Socket input as soon as the output is closed - Logging improvements --- src/Kestrel.Transport.Sockets/SocketConnection.cs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/Kestrel.Transport.Sockets/SocketConnection.cs b/src/Kestrel.Transport.Sockets/SocketConnection.cs index 43a3428fa..8d8956bd3 100644 --- a/src/Kestrel.Transport.Sockets/SocketConnection.cs +++ b/src/Kestrel.Transport.Sockets/SocketConnection.cs @@ -12,6 +12,7 @@ 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 { @@ -60,7 +61,6 @@ public async Task StartAsync(IConnectionHandler connectionHandler) if (await Task.WhenAny(receiveTask, sendTask) == sendTask) { // Tell the reader it's being aborted - _trace.ConnectionWriteFin(ConnectionId); _socket.Dispose(); } @@ -71,9 +71,9 @@ public async Task StartAsync(IConnectionHandler connectionHandler) // Dispose the socket(should noop if already called) _socket.Dispose(); } - catch (Exception) + catch (Exception ex) { - // TODO: Log + _trace.LogError(0, ex, $"Unexpected exception in {nameof(SocketConnection)}.{nameof(StartAsync)}."); } } @@ -225,7 +225,6 @@ private async Task DoSend() } } - _trace.ConnectionWriteFin(ConnectionId); _socket.Shutdown(SocketShutdown.Send); } catch (SocketException ex) when (ex.SocketErrorCode == SocketError.OperationAborted) @@ -246,7 +245,12 @@ private async Task DoSend() } finally { + // Now, complete the input so that no more reads can happen + Input.Complete(error ?? new ConnectionAbortedException()); Output.Complete(error); + + // Even if _socket.Shutdown() wasn't called above, StartAsync disposes _socket as soon as DoSend completes. + _trace.ConnectionWriteFin(ConnectionId); } } From 0e6125ae0cfba28f40ccb93369adaa836028d39d Mon Sep 17 00:00:00 2001 From: Stephen Halter <halter73@gmail.com> Date: Thu, 5 Oct 2017 19:02:01 -0700 Subject: [PATCH 07/15] Fix socket close ordering --- src/Kestrel.Transport.Sockets/SocketConnection.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Kestrel.Transport.Sockets/SocketConnection.cs b/src/Kestrel.Transport.Sockets/SocketConnection.cs index 8d8956bd3..e037019e2 100644 --- a/src/Kestrel.Transport.Sockets/SocketConnection.cs +++ b/src/Kestrel.Transport.Sockets/SocketConnection.cs @@ -18,12 +18,13 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets { internal sealed class SocketConnection : TransportConnection { + private const int MinAllocBufferSize = 2048; + private readonly Socket _socket; private readonly SocketTransport _transport; private readonly ISocketTrace _trace; private IList<ArraySegment<byte>> _sendBufferList; - private const int MinAllocBufferSize = 2048; internal SocketConnection(Socket socket, SocketTransport transport, ISocketTrace trace) { @@ -224,8 +225,6 @@ private async Task DoSend() Output.Advance(buffer.End); } } - - _socket.Shutdown(SocketShutdown.Send); } catch (SocketException ex) when (ex.SocketErrorCode == SocketError.OperationAborted) { @@ -249,8 +248,9 @@ private async Task DoSend() Input.Complete(error ?? new ConnectionAbortedException()); Output.Complete(error); - // Even if _socket.Shutdown() wasn't called above, StartAsync disposes _socket as soon as DoSend completes. + // Make sure to close the connection only after the input is aborted _trace.ConnectionWriteFin(ConnectionId); + _socket.Shutdown(SocketShutdown.Send); } } From 92c829a3833d29acd0cc90a292b7c237483e593c Mon Sep 17 00:00:00 2001 From: Stephen Halter <halter73@gmail.com> Date: Thu, 5 Oct 2017 19:10:23 -0700 Subject: [PATCH 08/15] Fix systemd activation tests to use libuv transport --- samples/SampleApp/Startup.cs | 18 ++++++++++++++---- src/Kestrel/Kestrel.csproj | 2 +- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/samples/SampleApp/Startup.cs b/samples/SampleApp/Startup.cs index 46758796e..562f3a020 100644 --- a/samples/SampleApp/Startup.cs +++ b/samples/SampleApp/Startup.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using System.Diagnostics; using System.Globalization; using System.IO; using System.Net; @@ -51,7 +52,7 @@ public static Task Main(string[] args) basePort = 5000; } - var host = new WebHostBuilder() + var hostBuilder = new WebHostBuilder() .ConfigureLogging((_, factory) => { factory.AddConsole(); @@ -81,10 +82,19 @@ public static Task Main(string[] args) //options.ListenUnixSocket("/tmp/kestrel-test.sock"); }) .UseContentRoot(Directory.GetCurrentDirectory()) - .UseStartup<Startup>() - .Build(); + .UseStartup<Startup>(); - return host.RunAsync(); + if (string.Equals(Process.GetCurrentProcess().Id.ToString(), Environment.GetEnvironmentVariable("LISTEN_PID"))) + { + // Use libuv if activated by systemd, since that's currently the only transport that supports being passed a socket handle. + hostBuilder.UseLibuv(options => + { + // Uncomment the following line to change the default number of libuv threads for all endpoints. + // options.ThreadCount = 4; + }); + } + + return hostBuilder.Build().RunAsync(); } } } \ No newline at end of file diff --git a/src/Kestrel/Kestrel.csproj b/src/Kestrel/Kestrel.csproj index 8cacb89c6..e2bf6bbda 100644 --- a/src/Kestrel/Kestrel.csproj +++ b/src/Kestrel/Kestrel.csproj @@ -17,7 +17,7 @@ <ItemGroup> <ProjectReference Include="..\Kestrel.Core\Kestrel.Core.csproj" /> <!-- Even though the Libuv transport is no longer used by default, it's remains for back-compat --> - <ProjectReference Include="..\Kestrel.Transport.Sockets\Kestrel.Transport.Libuv.csproj" /> + <ProjectReference Include="..\Kestrel.Transport.Libuv\Kestrel.Transport.Libuv.csproj" /> <ProjectReference Include="..\Kestrel.Transport.Sockets\Kestrel.Transport.Sockets.csproj" /> </ItemGroup> From e47da90e3d443eb713b59207056c3614d5bb7020 Mon Sep 17 00:00:00 2001 From: Stephen Halter <halter73@gmail.com> Date: Thu, 5 Oct 2017 19:10:41 -0700 Subject: [PATCH 09/15] Address PR feedback - Mostly fixed typos --- KestrelHttpServer.sln | 4 ++-- .../Internal/{ISocketTrace.cs => ISocketsTrace.cs} | 2 +- .../Internal/{SocketTrace.cs => SocketsTrace.cs} | 4 ++-- src/Kestrel.Transport.Sockets/SocketConnection.cs | 4 ++-- src/Kestrel.Transport.Sockets/SocketTransport.cs | 4 ++-- src/Kestrel.Transport.Sockets/SocketTransportFactory.cs | 4 ++-- src/Kestrel/Kestrel.csproj | 2 +- 7 files changed, 12 insertions(+), 12 deletions(-) rename src/Kestrel.Transport.Sockets/Internal/{ISocketTrace.cs => ISocketsTrace.cs} (93%) rename src/Kestrel.Transport.Sockets/Internal/{SocketTrace.cs => SocketsTrace.cs} (97%) diff --git a/KestrelHttpServer.sln b/KestrelHttpServer.sln index 1edf36770..2f3786bd6 100644 --- a/KestrelHttpServer.sln +++ b/KestrelHttpServer.sln @@ -117,9 +117,9 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SystemdActivation", "System test\SystemdActivation\Dockerfile = test\SystemdActivation\Dockerfile EndProjectSection EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Kestrel.Trasnport.Libuv.FunctionalTests", "test\Kestrel.Transport.Libuv.FunctionalTests\Kestrel.Trasnport.Libuv.FunctionalTests.csproj", "{74032D79-8EA7-4483-BD82-C38370420FFF}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Kestrel.Transport.Libuv.FunctionalTests", "test\Kestrel.Transport.Libuv.FunctionalTests\Kestrel.Trasnport.Libuv.FunctionalTests.csproj", "{74032D79-8EA7-4483-BD82-C38370420FFF}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Kestrel.Trasnport.Sockets.FunctionalTests", "test\Kestrel.Transport.Sockets.FunctionalTests\Kestrel.Trasnport.Sockets.FunctionalTests.csproj", "{9C7B6B5F-088A-436E-834B-6373EA36DEEE}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Kestrel.Transport.Sockets.FunctionalTests", "test\Kestrel.Transport.Sockets.FunctionalTests\Kestrel.Trasnport.Sockets.FunctionalTests.csproj", "{9C7B6B5F-088A-436E-834B-6373EA36DEEE}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution diff --git a/src/Kestrel.Transport.Sockets/Internal/ISocketTrace.cs b/src/Kestrel.Transport.Sockets/Internal/ISocketsTrace.cs similarity index 93% rename from src/Kestrel.Transport.Sockets/Internal/ISocketTrace.cs rename to src/Kestrel.Transport.Sockets/Internal/ISocketsTrace.cs index 668a98d7d..06c2c36f2 100644 --- a/src/Kestrel.Transport.Sockets/Internal/ISocketTrace.cs +++ b/src/Kestrel.Transport.Sockets/Internal/ISocketsTrace.cs @@ -6,7 +6,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets.Internal { - public interface ISocketTrace : ILogger + public interface ISocketsTrace : ILogger { void ConnectionReadFin(string connectionId); diff --git a/src/Kestrel.Transport.Sockets/Internal/SocketTrace.cs b/src/Kestrel.Transport.Sockets/Internal/SocketsTrace.cs similarity index 97% rename from src/Kestrel.Transport.Sockets/Internal/SocketTrace.cs rename to src/Kestrel.Transport.Sockets/Internal/SocketsTrace.cs index 6270bed5c..2757bcf4c 100644 --- a/src/Kestrel.Transport.Sockets/Internal/SocketTrace.cs +++ b/src/Kestrel.Transport.Sockets/Internal/SocketsTrace.cs @@ -6,7 +6,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets.Internal { - public class SocketTrace : ISocketTrace + public class SocketsTrace : ISocketsTrace { // ConnectionRead: Reserved: 3 @@ -30,7 +30,7 @@ public class SocketTrace : ISocketTrace private readonly ILogger _logger; - public SocketTrace(ILogger logger) + public SocketsTrace(ILogger logger) { _logger = logger; } diff --git a/src/Kestrel.Transport.Sockets/SocketConnection.cs b/src/Kestrel.Transport.Sockets/SocketConnection.cs index e037019e2..2b9ac1987 100644 --- a/src/Kestrel.Transport.Sockets/SocketConnection.cs +++ b/src/Kestrel.Transport.Sockets/SocketConnection.cs @@ -22,11 +22,11 @@ internal sealed class SocketConnection : TransportConnection private readonly Socket _socket; private readonly SocketTransport _transport; - private readonly ISocketTrace _trace; + private readonly ISocketsTrace _trace; private IList<ArraySegment<byte>> _sendBufferList; - internal SocketConnection(Socket socket, SocketTransport transport, ISocketTrace trace) + internal SocketConnection(Socket socket, SocketTransport transport, ISocketsTrace trace) { Debug.Assert(socket != null); Debug.Assert(transport != null); diff --git a/src/Kestrel.Transport.Sockets/SocketTransport.cs b/src/Kestrel.Transport.Sockets/SocketTransport.cs index ab2c69eef..af5c43093 100644 --- a/src/Kestrel.Transport.Sockets/SocketTransport.cs +++ b/src/Kestrel.Transport.Sockets/SocketTransport.cs @@ -17,7 +17,7 @@ internal sealed class SocketTransport : ITransport private readonly SocketTransportFactory _transportFactory; private readonly IEndPointInformation _endPointInformation; private readonly IConnectionHandler _handler; - private readonly ISocketTrace _trace; + private readonly ISocketsTrace _trace; private Socket _listenSocket; private Task _listenTask; @@ -25,7 +25,7 @@ internal SocketTransport( SocketTransportFactory transportFactory, IEndPointInformation endPointInformation, IConnectionHandler handler, - ISocketTrace trace) + ISocketsTrace trace) { Debug.Assert(transportFactory != null); Debug.Assert(endPointInformation != null); diff --git a/src/Kestrel.Transport.Sockets/SocketTransportFactory.cs b/src/Kestrel.Transport.Sockets/SocketTransportFactory.cs index f4620951e..399e857a9 100644 --- a/src/Kestrel.Transport.Sockets/SocketTransportFactory.cs +++ b/src/Kestrel.Transport.Sockets/SocketTransportFactory.cs @@ -13,7 +13,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets public sealed class SocketTransportFactory : ITransportFactory { private readonly PipeFactory _pipeFactory = new PipeFactory(); - private readonly SocketTrace _trace; + private readonly SocketsTrace _trace; public SocketTransportFactory( IOptions<SocketTransportOptions> options, @@ -29,7 +29,7 @@ public SocketTransportFactory( } var logger = loggerFactory.CreateLogger("Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets"); - _trace = new SocketTrace(logger); + _trace = new SocketsTrace(logger); } public ITransport Create(IEndPointInformation endPointInformation, IConnectionHandler handler) diff --git a/src/Kestrel/Kestrel.csproj b/src/Kestrel/Kestrel.csproj index e2bf6bbda..1376f1e89 100644 --- a/src/Kestrel/Kestrel.csproj +++ b/src/Kestrel/Kestrel.csproj @@ -16,7 +16,7 @@ <ItemGroup> <ProjectReference Include="..\Kestrel.Core\Kestrel.Core.csproj" /> - <!-- Even though the Libuv transport is no longer used by default, it's remains for back-compat --> + <!-- Even though the Libuv transport is no longer used by default, it remains for back-compat --> <ProjectReference Include="..\Kestrel.Transport.Libuv\Kestrel.Transport.Libuv.csproj" /> <ProjectReference Include="..\Kestrel.Transport.Sockets\Kestrel.Transport.Sockets.csproj" /> </ItemGroup> From 7f88ba87460393123e76aed686d89447a88e0b5a Mon Sep 17 00:00:00 2001 From: Stephen Halter <halter73@gmail.com> Date: Fri, 6 Oct 2017 16:34:41 -0700 Subject: [PATCH 10/15] Fully close socket after server-side abort --- src/Kestrel.Transport.Sockets/SocketConnection.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Kestrel.Transport.Sockets/SocketConnection.cs b/src/Kestrel.Transport.Sockets/SocketConnection.cs index 2b9ac1987..9e6470af8 100644 --- a/src/Kestrel.Transport.Sockets/SocketConnection.cs +++ b/src/Kestrel.Transport.Sockets/SocketConnection.cs @@ -46,6 +46,10 @@ internal SocketConnection(Socket socket, SocketTransport transport, ISocketsTrac RemotePort = remoteEndPoint.Port; } + public override PipeFactory PipeFactory => _transport.TransportFactory.PipeFactory; + public override IScheduler InputWriterScheduler => InlineScheduler.Default; + public override IScheduler OutputReaderScheduler => TaskRunScheduler.Default; + public async Task StartAsync(IConnectionHandler connectionHandler) { try @@ -245,12 +249,11 @@ private async Task DoSend() finally { // Now, complete the input so that no more reads can happen - Input.Complete(error ?? new ConnectionAbortedException()); Output.Complete(error); // Make sure to close the connection only after the input is aborted _trace.ConnectionWriteFin(ConnectionId); - _socket.Shutdown(SocketShutdown.Send); + _socket.Shutdown(SocketShutdown.Both); } } @@ -264,8 +267,5 @@ private static ArraySegment<byte> GetArraySegment(Buffer<byte> buffer) return segment; } - public override PipeFactory PipeFactory => _transport.TransportFactory.PipeFactory; - public override IScheduler InputWriterScheduler => InlineScheduler.Default; - public override IScheduler OutputReaderScheduler => TaskRunScheduler.Default; } } From 9707939703660bbc31e9250b8a4202e9df752a2e Mon Sep 17 00:00:00 2001 From: Stephen Halter <halter73@gmail.com> Date: Fri, 6 Oct 2017 16:56:57 -0700 Subject: [PATCH 11/15] Dispose Sockets PipeFactory --- src/Kestrel.Transport.Sockets/SocketConnection.cs | 9 ++++----- src/Kestrel.Transport.Sockets/SocketTransport.cs | 11 ++++------- .../SocketTransportFactory.cs | 6 +----- 3 files changed, 9 insertions(+), 17 deletions(-) diff --git a/src/Kestrel.Transport.Sockets/SocketConnection.cs b/src/Kestrel.Transport.Sockets/SocketConnection.cs index 9e6470af8..d7d06aaa0 100644 --- a/src/Kestrel.Transport.Sockets/SocketConnection.cs +++ b/src/Kestrel.Transport.Sockets/SocketConnection.cs @@ -21,19 +21,18 @@ internal sealed class SocketConnection : TransportConnection private const int MinAllocBufferSize = 2048; private readonly Socket _socket; - private readonly SocketTransport _transport; private readonly ISocketsTrace _trace; private IList<ArraySegment<byte>> _sendBufferList; - internal SocketConnection(Socket socket, SocketTransport transport, ISocketsTrace trace) + internal SocketConnection(Socket socket, PipeFactory pipeFactory, ISocketsTrace trace) { Debug.Assert(socket != null); - Debug.Assert(transport != null); + Debug.Assert(pipeFactory != null); Debug.Assert(trace != null); _socket = socket; - _transport = transport; + PipeFactory = pipeFactory; _trace = trace; var localEndPoint = (IPEndPoint)_socket.LocalEndPoint; @@ -46,7 +45,7 @@ internal SocketConnection(Socket socket, SocketTransport transport, ISocketsTrac RemotePort = remoteEndPoint.Port; } - public override PipeFactory PipeFactory => _transport.TransportFactory.PipeFactory; + public override PipeFactory PipeFactory { get; } public override IScheduler InputWriterScheduler => InlineScheduler.Default; public override IScheduler OutputReaderScheduler => TaskRunScheduler.Default; diff --git a/src/Kestrel.Transport.Sockets/SocketTransport.cs b/src/Kestrel.Transport.Sockets/SocketTransport.cs index af5c43093..61ede997e 100644 --- a/src/Kestrel.Transport.Sockets/SocketTransport.cs +++ b/src/Kestrel.Transport.Sockets/SocketTransport.cs @@ -3,6 +3,7 @@ using System; using System.Diagnostics; +using System.IO.Pipelines; using System.Net; using System.Net.Sockets; using System.Threading.Tasks; @@ -14,7 +15,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets { internal sealed class SocketTransport : ITransport { - private readonly SocketTransportFactory _transportFactory; + private readonly PipeFactory _pipeFactory = new PipeFactory(); private readonly IEndPointInformation _endPointInformation; private readonly IConnectionHandler _handler; private readonly ISocketsTrace _trace; @@ -22,18 +23,15 @@ internal sealed class SocketTransport : ITransport private Task _listenTask; internal SocketTransport( - SocketTransportFactory transportFactory, IEndPointInformation endPointInformation, IConnectionHandler handler, ISocketsTrace trace) { - Debug.Assert(transportFactory != null); Debug.Assert(endPointInformation != null); Debug.Assert(endPointInformation.Type == ListenType.IPEndPoint); Debug.Assert(handler != null); Debug.Assert(trace != null); - _transportFactory = transportFactory; _endPointInformation = endPointInformation; _handler = handler; _trace = trace; @@ -100,6 +98,7 @@ public async Task UnbindAsync() public Task StopAsync() { + _pipeFactory.Dispose(); return Task.CompletedTask; } @@ -113,7 +112,7 @@ private async Task RunAcceptLoopAsync() acceptSocket.NoDelay = _endPointInformation.NoDelay; - var connection = new SocketConnection(acceptSocket, this, _trace); + var connection = new SocketConnection(acceptSocket, _pipeFactory, _trace); _ = connection.StartAsync(_handler); } } @@ -129,7 +128,5 @@ private async Task RunAcceptLoopAsync() } } } - - internal SocketTransportFactory TransportFactory => _transportFactory; } } diff --git a/src/Kestrel.Transport.Sockets/SocketTransportFactory.cs b/src/Kestrel.Transport.Sockets/SocketTransportFactory.cs index 399e857a9..b8ee97791 100644 --- a/src/Kestrel.Transport.Sockets/SocketTransportFactory.cs +++ b/src/Kestrel.Transport.Sockets/SocketTransportFactory.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; -using System.IO.Pipelines; using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal; using Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets.Internal; using Microsoft.Extensions.Options; @@ -12,7 +11,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets { public sealed class SocketTransportFactory : ITransportFactory { - private readonly PipeFactory _pipeFactory = new PipeFactory(); private readonly SocketsTrace _trace; public SocketTransportFactory( @@ -49,9 +47,7 @@ public ITransport Create(IEndPointInformation endPointInformation, IConnectionHa throw new ArgumentNullException(nameof(handler)); } - return new SocketTransport(this, endPointInformation, handler, _trace); + return new SocketTransport(endPointInformation, handler, _trace); } - - internal PipeFactory PipeFactory => _pipeFactory; } } From b49fde0a2a3c3da1edf818ad8280e02f0befe86b Mon Sep 17 00:00:00 2001 From: Stephen Halter <halter73@gmail.com> Date: Mon, 9 Oct 2017 12:50:47 -0700 Subject: [PATCH 12/15] Improve Socket's server-side abort handling --- src/Kestrel.Transport.Sockets/SocketConnection.cs | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/Kestrel.Transport.Sockets/SocketConnection.cs b/src/Kestrel.Transport.Sockets/SocketConnection.cs index d7d06aaa0..e0112d6cf 100644 --- a/src/Kestrel.Transport.Sockets/SocketConnection.cs +++ b/src/Kestrel.Transport.Sockets/SocketConnection.cs @@ -24,6 +24,7 @@ internal sealed class SocketConnection : TransportConnection private readonly ISocketsTrace _trace; private IList<ArraySegment<byte>> _sendBufferList; + private volatile bool _aborted; internal SocketConnection(Socket socket, PipeFactory pipeFactory, ISocketsTrace trace) { @@ -134,7 +135,8 @@ private async Task DoReceive() error = new ConnectionResetException(ex.Message, ex); _trace.ConnectionReset(ConnectionId); } - catch (SocketException ex) when (ex.SocketErrorCode == SocketError.OperationAborted) + catch (SocketException ex) when (ex.SocketErrorCode == SocketError.OperationAborted || + ex.SocketErrorCode == SocketError.Interrupted) { error = new ConnectionAbortedException(); _trace.ConnectionError(ConnectionId, error); @@ -156,6 +158,11 @@ private async Task DoReceive() } finally { + if (_aborted) + { + error = error ?? new ConnectionAbortedException(); + } + Input.Complete(error); } } @@ -247,10 +254,12 @@ private async Task DoSend() } finally { - // Now, complete the input so that no more reads can happen Output.Complete(error); - // Make sure to close the connection only after the input is aborted + // Make sure to close the connection only after the _aborted flag is set. + // Without this, the RequestsCanBeAbortedMidRead test will sometimes fail when + // a BadHttpRequestException is thrown instaed of a TaskCanceledException. + _aborted = true; _trace.ConnectionWriteFin(ConnectionId); _socket.Shutdown(SocketShutdown.Both); } From f157b4ae6677f36326ca57c3a2319854a7b3fd1d Mon Sep 17 00:00:00 2001 From: Stephen Halter <halter73@gmail.com> Date: Mon, 9 Oct 2017 17:02:53 -0700 Subject: [PATCH 13/15] Fix typo --- src/Kestrel.Transport.Sockets/SocketConnection.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Kestrel.Transport.Sockets/SocketConnection.cs b/src/Kestrel.Transport.Sockets/SocketConnection.cs index e0112d6cf..6a7f765af 100644 --- a/src/Kestrel.Transport.Sockets/SocketConnection.cs +++ b/src/Kestrel.Transport.Sockets/SocketConnection.cs @@ -258,7 +258,7 @@ private async Task DoSend() // Make sure to close the connection only after the _aborted flag is set. // Without this, the RequestsCanBeAbortedMidRead test will sometimes fail when - // a BadHttpRequestException is thrown instaed of a TaskCanceledException. + // a BadHttpRequestException is thrown instead of a TaskCanceledException. _aborted = true; _trace.ConnectionWriteFin(ConnectionId); _socket.Shutdown(SocketShutdown.Both); From ac6350bf0718eb308b710be2ab06cb7ea16adc33 Mon Sep 17 00:00:00 2001 From: Stephen Halter <halter73@gmail.com> Date: Tue, 10 Oct 2017 14:53:21 -0700 Subject: [PATCH 14/15] Prevent port 5000 binding errors on macOS --- test/Kestrel.FunctionalTests/AddressRegistrationTests.cs | 6 ++++++ .../Kestrel.Trasnport.Sockets.FunctionalTests.csproj | 1 + 2 files changed, 7 insertions(+) diff --git a/test/Kestrel.FunctionalTests/AddressRegistrationTests.cs b/test/Kestrel.FunctionalTests/AddressRegistrationTests.cs index e2e7bea98..21237c26a 100644 --- a/test/Kestrel.FunctionalTests/AddressRegistrationTests.cs +++ b/test/Kestrel.FunctionalTests/AddressRegistrationTests.cs @@ -807,6 +807,11 @@ public PortSupportedConditionAttribute(int port) 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. + return false; +#else try { using (var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) @@ -819,6 +824,7 @@ private bool CanBindToPort() { return false; } +#endif } } } diff --git a/test/Kestrel.Transport.Sockets.FunctionalTests/Kestrel.Trasnport.Sockets.FunctionalTests.csproj b/test/Kestrel.Transport.Sockets.FunctionalTests/Kestrel.Trasnport.Sockets.FunctionalTests.csproj index 731a6c363..8030d1846 100644 --- a/test/Kestrel.Transport.Sockets.FunctionalTests/Kestrel.Trasnport.Sockets.FunctionalTests.csproj +++ b/test/Kestrel.Transport.Sockets.FunctionalTests/Kestrel.Trasnport.Sockets.FunctionalTests.csproj @@ -6,6 +6,7 @@ <TargetFrameworks>netcoreapp2.0;net461</TargetFrameworks> <TargetFrameworks Condition=" '$(OS)' != 'Windows_NT' ">netcoreapp2.0</TargetFrameworks> <DefineConstants Condition="$([MSBuild]::IsOSPlatform('OSX'))">$(DefineConstants);MACOS</DefineConstants> + <DefineConstants>$(DefineConstants);SOCKETS</DefineConstants> <ServerGarbageCollection>true</ServerGarbageCollection> </PropertyGroup> From adfcc825c2989e53a71a8b5390cd5c211804c5f2 Mon Sep 17 00:00:00 2001 From: Stephen Halter <halter73@gmail.com> Date: Tue, 10 Oct 2017 17:08:45 -0700 Subject: [PATCH 15/15] Add explicit rebinding test --- .../AddressRegistrationTests.cs | 86 +++++++++++++++++++ 1 file changed, 86 insertions(+) diff --git a/test/Kestrel.FunctionalTests/AddressRegistrationTests.cs b/test/Kestrel.FunctionalTests/AddressRegistrationTests.cs index 21237c26a..66f2a62c2 100644 --- a/test/Kestrel.FunctionalTests/AddressRegistrationTests.cs +++ b/test/Kestrel.FunctionalTests/AddressRegistrationTests.cs @@ -526,6 +526,91 @@ public void ThrowsForUnsupportedAddressFromHosting(string addr) } } + // 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.")] + public async Task CanRebindToEndPoint() + { + var port = GetNextPort(); + var endPointAddress = $"http://127.0.0.1:{port}/"; + + var hostBuilder = TransportSelector.GetWebHostBuilder() + .ConfigureLogging(_configureLoggingDelegate) + .UseKestrel(options => + { + options.Listen(IPAddress.Loopback, port); + }) + .Configure(ConfigureEchoAddress); + + using (var host = hostBuilder.Build()) + { + host.Start(); + + Assert.Equal(endPointAddress, await HttpClientSlim.GetStringAsync(endPointAddress)); + } + + hostBuilder = TransportSelector.GetWebHostBuilder() + .ConfigureLogging(_configureLoggingDelegate) + .UseKestrel(options => + { + options.Listen(IPAddress.Loopback, port); + }) + .Configure(ConfigureEchoAddress); + + using (var host = hostBuilder.Build()) + { + host.Start(); + + Assert.Equal(endPointAddress, await HttpClientSlim.GetStringAsync(endPointAddress)); + } + } + + // 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.")] + public async Task CanRebindToMultipleEndPoints() + { + var port = GetNextPort(); + var ipv4endPointAddress = $"http://127.0.0.1:{port}/"; + var ipv6endPointAddress = $"http://[::1]:{port}/"; + + var hostBuilder = TransportSelector.GetWebHostBuilder() + .ConfigureLogging(_configureLoggingDelegate) + .UseKestrel(options => + { + options.Listen(IPAddress.Loopback, port); + options.Listen(IPAddress.IPv6Loopback, port); + }) + .Configure(ConfigureEchoAddress); + + using (var host = hostBuilder.Build()) + { + host.Start(); + + Assert.Equal(ipv4endPointAddress, await HttpClientSlim.GetStringAsync(ipv4endPointAddress)); + Assert.Equal(ipv6endPointAddress, await HttpClientSlim.GetStringAsync(ipv6endPointAddress)); + } + + hostBuilder = TransportSelector.GetWebHostBuilder() + .ConfigureLogging(_configureLoggingDelegate) + .UseKestrel(options => + { + options.Listen(IPAddress.Loopback, port); + options.Listen(IPAddress.IPv6Loopback, port); + }) + .Configure(ConfigureEchoAddress); + + using (var host = hostBuilder.Build()) + { + host.Start(); + + Assert.Equal(ipv4endPointAddress, await HttpClientSlim.GetStringAsync(ipv4endPointAddress)); + Assert.Equal(ipv6endPointAddress, await HttpClientSlim.GetStringAsync(ipv6endPointAddress)); + } + } + private void ThrowsWhenBindingLocalhostToAddressInUse(AddressFamily addressFamily, IPAddress address) { using (var socket = new Socket(addressFamily, SocketType.Stream, ProtocolType.Tcp)) @@ -810,6 +895,7 @@ 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