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