diff --git a/KestrelHttpServer.sln b/KestrelHttpServer.sln
index a2fc4e3108..2fba69b809 100644
--- a/KestrelHttpServer.sln
+++ b/KestrelHttpServer.sln
@@ -62,6 +62,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Server
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Server.Kestrel", "src\Microsoft.AspNetCore.Server.Kestrel\Microsoft.AspNetCore.Server.Kestrel.csproj", "{56139957-5C29-4E7D-89BD-7D20598B4EAF}"
EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets", "src\Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets\Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets.csproj", "{6950B18F-A3D2-41A4-AFEC-8B7C49517611}"
+EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions", "src\Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions\Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.csproj", "{2E9CB89D-EC8F-4DD9-A72B-08D5BABF752D}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Tests", "test\Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Tests\Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Tests.csproj", "{D95A7EC3-48AC-4D03-B2E2-0DA3E13BD3A4}"
@@ -198,6 +200,18 @@ Global
{56139957-5C29-4E7D-89BD-7D20598B4EAF}.Release|x64.Build.0 = Release|Any CPU
{56139957-5C29-4E7D-89BD-7D20598B4EAF}.Release|x86.ActiveCfg = Release|Any CPU
{56139957-5C29-4E7D-89BD-7D20598B4EAF}.Release|x86.Build.0 = Release|Any CPU
+ {6950B18F-A3D2-41A4-AFEC-8B7C49517611}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {6950B18F-A3D2-41A4-AFEC-8B7C49517611}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {6950B18F-A3D2-41A4-AFEC-8B7C49517611}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {6950B18F-A3D2-41A4-AFEC-8B7C49517611}.Debug|x64.Build.0 = Debug|Any CPU
+ {6950B18F-A3D2-41A4-AFEC-8B7C49517611}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {6950B18F-A3D2-41A4-AFEC-8B7C49517611}.Debug|x86.Build.0 = Debug|Any CPU
+ {6950B18F-A3D2-41A4-AFEC-8B7C49517611}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {6950B18F-A3D2-41A4-AFEC-8B7C49517611}.Release|Any CPU.Build.0 = Release|Any CPU
+ {6950B18F-A3D2-41A4-AFEC-8B7C49517611}.Release|x64.ActiveCfg = Release|Any CPU
+ {6950B18F-A3D2-41A4-AFEC-8B7C49517611}.Release|x64.Build.0 = Release|Any CPU
+ {6950B18F-A3D2-41A4-AFEC-8B7C49517611}.Release|x86.ActiveCfg = Release|Any CPU
+ {6950B18F-A3D2-41A4-AFEC-8B7C49517611}.Release|x86.Build.0 = Release|Any CPU
{2E9CB89D-EC8F-4DD9-A72B-08D5BABF752D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2E9CB89D-EC8F-4DD9-A72B-08D5BABF752D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2E9CB89D-EC8F-4DD9-A72B-08D5BABF752D}.Debug|x64.ActiveCfg = Debug|Any CPU
@@ -251,6 +265,7 @@ Global
{2822C132-BFFB-4D53-AC5B-E7E47DD81A6E} = {0EF2ACDF-012F-4472-A13A-4272419E2903}
{A76B8C8C-0DC5-4DD3-9B1F-02E51A0915F4} = {2D5D5227-4DBD-499A-96B1-76A36B03B750}
{56139957-5C29-4E7D-89BD-7D20598B4EAF} = {2D5D5227-4DBD-499A-96B1-76A36B03B750}
+ {6950B18F-A3D2-41A4-AFEC-8B7C49517611} = {2D5D5227-4DBD-499A-96B1-76A36B03B750}
{2E9CB89D-EC8F-4DD9-A72B-08D5BABF752D} = {2D5D5227-4DBD-499A-96B1-76A36B03B750}
{D95A7EC3-48AC-4D03-B2E2-0DA3E13BD3A4} = {D3273454-EA07-41D2-BF0B-FCC3675C2483}
{4F1C30F8-CCAA-48D7-9DF6-2A84021F5BCC} = {D3273454-EA07-41D2-BF0B-FCC3675C2483}
diff --git a/src/Microsoft.AspNetCore.Server.Kestrel.Core/Internal/ConnectionHandler.cs b/src/Microsoft.AspNetCore.Server.Kestrel.Core/Internal/ConnectionHandler.cs
index db9236d9d8..0ea0494d0e 100644
--- a/src/Microsoft.AspNetCore.Server.Kestrel.Core/Internal/ConnectionHandler.cs
+++ b/src/Microsoft.AspNetCore.Server.Kestrel.Core/Internal/ConnectionHandler.cs
@@ -27,8 +27,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
public IConnectionContext OnConnection(IConnectionInformation connectionInfo)
{
- var inputPipe = connectionInfo.PipeFactory.Create(GetInputPipeOptions(connectionInfo.InputWriterScheduler));
- var outputPipe = connectionInfo.PipeFactory.Create(GetOutputPipeOptions(connectionInfo.OutputReaderScheduler));
+ var inputPipe = connectionInfo.PipeFactory.Create(GetInputPipeOptions(requiresDispatch: connectionInfo.RequiresDispatch, writerScheduler: connectionInfo.InputWriterScheduler));
+ var outputPipe = connectionInfo.PipeFactory.Create(GetOutputPipeOptions(requiresDispatch: connectionInfo.RequiresDispatch, readerScheduler: connectionInfo.OutputReaderScheduler));
var connectionId = CorrelationIdGenerator.GetNextId();
var frameConnectionId = Interlocked.Increment(ref _lastFrameConnectionId);
@@ -70,18 +70,18 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
}
// Internal for testing
- internal PipeOptions GetInputPipeOptions(IScheduler writerScheduler) => new PipeOptions
+ internal PipeOptions GetInputPipeOptions(bool requiresDispatch, IScheduler writerScheduler) => new PipeOptions
{
- ReaderScheduler = _serviceContext.ThreadPool,
+ ReaderScheduler = (requiresDispatch ? (IScheduler)_serviceContext.ThreadPool : (IScheduler)InlineScheduler.Default),
WriterScheduler = writerScheduler,
MaximumSizeHigh = _serviceContext.ServerOptions.Limits.MaxRequestBufferSize ?? 0,
MaximumSizeLow = _serviceContext.ServerOptions.Limits.MaxRequestBufferSize ?? 0
};
- internal PipeOptions GetOutputPipeOptions(IScheduler readerScheduler) => new PipeOptions
+ internal PipeOptions GetOutputPipeOptions(bool requiresDispatch, IScheduler readerScheduler) => new PipeOptions
{
ReaderScheduler = readerScheduler,
- WriterScheduler = _serviceContext.ThreadPool,
+ WriterScheduler = (requiresDispatch ? (IScheduler)_serviceContext.ThreadPool : (IScheduler)InlineScheduler.Default),
MaximumSizeHigh = GetOutputResponseBufferSize(),
MaximumSizeLow = GetOutputResponseBufferSize()
};
diff --git a/src/Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions/IConnectionInformation.cs b/src/Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions/IConnectionInformation.cs
index a06bd16dd9..ca3cd9afbe 100644
--- a/src/Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions/IConnectionInformation.cs
+++ b/src/Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions/IConnectionInformation.cs
@@ -12,6 +12,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions
IPEndPoint LocalEndPoint { get; }
PipeFactory PipeFactory { get; }
+
+ bool RequiresDispatch { get; }
+
IScheduler InputWriterScheduler { get; }
IScheduler OutputReaderScheduler { get; }
}
diff --git a/src/Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv/Internal/LibuvConnectionContext.cs b/src/Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv/Internal/LibuvConnectionContext.cs
index 6c9d0cfcf5..b28739ed2e 100644
--- a/src/Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv/Internal/LibuvConnectionContext.cs
+++ b/src/Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv/Internal/LibuvConnectionContext.cs
@@ -24,6 +24,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal
public IPEndPoint LocalEndPoint { get; set; }
public PipeFactory PipeFactory => ListenerContext.Thread.PipeFactory;
+ public bool RequiresDispatch => true;
public IScheduler InputWriterScheduler => ListenerContext.Thread;
public IScheduler OutputReaderScheduler => ListenerContext.Thread;
}
diff --git a/src/Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets/Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets.csproj b/src/Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets/Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets.csproj
new file mode 100644
index 0000000000..911f42f7b2
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets/Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets.csproj
@@ -0,0 +1,24 @@
+
+
+
+
+
+ Managed socket transport for the ASP.NET Core Kestrel cross-platform web server.
+ netstandard1.3
+ true
+ aspnetcore;kestrel
+ true
+ CS1591;$(NoWarn)
+ false
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets/SocketConnection.cs b/src/Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets/SocketConnection.cs
new file mode 100644
index 0000000000..922fb53186
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets/SocketConnection.cs
@@ -0,0 +1,216 @@
+// 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.Server.Kestrel.Internal.System.Buffers;
+using Microsoft.AspNetCore.Server.Kestrel.Internal.System.IO.Pipelines;
+using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions;
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Net;
+using System.Net.Sockets;
+using System.Threading.Tasks;
+
+namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets
+{
+ internal sealed class SocketConnection : IConnectionInformation
+ {
+ private readonly Socket _socket;
+ private readonly SocketTransport _transport;
+ private readonly IPEndPoint _localEndPoint;
+ private readonly IPEndPoint _remoteEndPoint;
+ private IConnectionContext _connectionContext;
+ private IPipeWriter _input;
+ private IPipeReader _output;
+ private IList> _sendBufferList;
+
+ private const int MinAllocBufferSize = 2048; // from libuv transport
+
+ internal SocketConnection(Socket socket, SocketTransport transport)
+ {
+ Debug.Assert(socket != null);
+ Debug.Assert(transport != null);
+
+ _socket = socket;
+ _transport = transport;
+
+ _localEndPoint = (IPEndPoint)_socket.LocalEndPoint;
+ _remoteEndPoint = (IPEndPoint)_socket.RemoteEndPoint;
+ }
+
+ public async void Start(IConnectionHandler connectionHandler)
+ {
+ _connectionContext = connectionHandler.OnConnection(this);
+
+ _input = _connectionContext.Input;
+ _output = _connectionContext.Output;
+
+ // Spawn send and receive logic
+ Task receiveTask = DoReceive();
+ Task sendTask = DoSend();
+
+ // Wait for them to complete (note they won't throw exceptions)
+ await receiveTask;
+ await sendTask;
+
+ _socket.Dispose();
+
+ _connectionContext.OnConnectionClosed();
+ }
+
+ private async Task DoReceive()
+ {
+ try
+ {
+ bool done = false;
+ while (!done)
+ {
+ // Ensure we have some reasonable amount of buffer space
+ WritableBuffer buffer = _input.Alloc(MinAllocBufferSize);
+
+ int bytesReceived;
+ try
+ {
+ bytesReceived = await _socket.ReceiveAsync(GetArraySegment(buffer.Buffer), SocketFlags.None);
+ }
+ catch (Exception ex)
+ {
+ buffer.Commit();
+ _connectionContext.Abort(ex);
+ _input.Complete(ex);
+ break;
+ }
+
+ if (bytesReceived == 0)
+ {
+ // EOF
+ Exception ex = new TaskCanceledException();
+ buffer.Commit();
+ _connectionContext.Abort(ex);
+ _input.Complete(ex);
+ break;
+ }
+
+ // record what data we filled into the buffer and push to pipe
+ buffer.Advance(bytesReceived);
+ var result = await buffer.FlushAsync();
+ if (result.IsCompleted)
+ {
+ // Pipe consumer is shut down
+ _socket.Shutdown(SocketShutdown.Receive);
+ done = true;
+ }
+ }
+ }
+ catch (Exception)
+ {
+ // We don't expect any exceptions here, but eat it anyway as caller does not handle this.
+ Debug.Assert(false);
+ }
+ }
+
+ private void SetupSendBuffers(ReadableBuffer buffer)
+ {
+ Debug.Assert(!buffer.IsEmpty);
+ Debug.Assert(!buffer.IsSingleSpan);
+
+ if (_sendBufferList == null)
+ {
+ _sendBufferList = new List>();
+ }
+
+ // We should always clear the list after the send
+ Debug.Assert(_sendBufferList.Count == 0);
+
+ foreach (var b in buffer)
+ {
+ _sendBufferList.Add(GetArraySegment(b));
+ }
+ }
+
+ private async Task DoSend()
+ {
+ try
+ {
+ bool done = false;
+ while (!done)
+ {
+ // Wait for data to write from the pipe producer
+ ReadResult result = await _output.ReadAsync();
+ ReadableBuffer buffer = result.Buffer;
+
+ try
+ {
+ if (!buffer.IsEmpty)
+ {
+ if (buffer.IsSingleSpan)
+ {
+ await _socket.SendAsync(GetArraySegment(buffer.First), SocketFlags.None);
+ }
+ else
+ {
+ SetupSendBuffers(buffer);
+ try
+ {
+ await _socket.SendAsync(_sendBufferList, SocketFlags.None);
+ }
+ finally
+ {
+ _sendBufferList.Clear();
+ }
+ }
+ }
+
+ if (result.IsCancelled)
+ {
+ // Send a FIN
+ _socket.Shutdown(SocketShutdown.Send);
+ break;
+ }
+
+ if (buffer.IsEmpty && result.IsCompleted)
+ {
+ // Send a FIN
+ _socket.Shutdown(SocketShutdown.Send);
+ break;
+ }
+ }
+ finally
+ {
+ _output.Advance(buffer.End);
+ }
+ }
+
+ // We're done reading
+ _output.Complete();
+ }
+ catch (Exception ex)
+ {
+ _output.Complete(ex);
+ }
+ }
+
+ private static ArraySegment GetArraySegment(Buffer buffer)
+ {
+ ArraySegment segment;
+ if (!buffer.TryGetArray(out segment))
+ {
+ throw new InvalidOperationException();
+ }
+
+ return segment;
+ }
+
+ public IPEndPoint RemoteEndPoint => _remoteEndPoint;
+
+ public IPEndPoint LocalEndPoint => _localEndPoint;
+
+ public PipeFactory PipeFactory => _transport.TransportFactory.PipeFactory;
+
+ public bool RequiresDispatch => _transport.TransportFactory.ForceDispatch;
+
+ public IScheduler InputWriterScheduler => InlineScheduler.Default;
+
+ public IScheduler OutputReaderScheduler => InlineScheduler.Default;
+ }
+}
diff --git a/src/Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets/SocketTransport.cs b/src/Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets/SocketTransport.cs
new file mode 100644
index 0000000000..0a7aaa15ae
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets/SocketTransport.cs
@@ -0,0 +1,127 @@
+// 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.Server.Kestrel.Internal.System.IO.Pipelines;
+using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions;
+using System;
+using System.Diagnostics;
+using System.Net;
+using System.Net.Sockets;
+using System.Threading.Tasks;
+
+namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets
+{
+ internal sealed class SocketTransport : ITransport
+ {
+ private readonly SocketTransportFactory _transportFactory;
+ private readonly IEndPointInformation _endPointInformation;
+ private readonly IConnectionHandler _handler;
+ private Socket _listenSocket;
+ private Task _listenTask;
+
+ internal SocketTransport(SocketTransportFactory transportFactory, IEndPointInformation endPointInformation, IConnectionHandler handler)
+ {
+ Debug.Assert(transportFactory != null);
+ Debug.Assert(endPointInformation != null);
+ Debug.Assert(endPointInformation.Type == ListenType.IPEndPoint);
+ Debug.Assert(handler != null);
+
+ _transportFactory = transportFactory;
+ _endPointInformation = endPointInformation;
+ _handler = handler;
+
+ _listenSocket = null;
+ _listenTask = null;
+ }
+
+ public Task BindAsync()
+ {
+ if (_listenSocket != null)
+ {
+ throw new InvalidOperationException("Transport is already bound");
+ }
+
+ IPEndPoint endPoint = _endPointInformation.IPEndPoint;
+
+ var listenSocket = new Socket(endPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
+
+ // Kestrel expects IPv6Any to bind to both IPv6 and IPv4
+ if (endPoint.Address == IPAddress.IPv6Any)
+ {
+ listenSocket.DualMode = true;
+ }
+
+ try
+ {
+ listenSocket.Bind(endPoint);
+ }
+ catch (SocketException e) when (e.SocketErrorCode == SocketError.AddressAlreadyInUse)
+ {
+ throw new AddressInUseException(e.Message, e);
+ }
+
+ // If requested port was "0", replace with assigned dynamic port.
+ if (_endPointInformation.IPEndPoint.Port == 0)
+ {
+ _endPointInformation.IPEndPoint = (IPEndPoint)listenSocket.LocalEndPoint;
+ }
+
+ listenSocket.Listen(512);
+
+ _listenSocket = listenSocket;
+
+ _listenTask = Task.Run(() => RunAcceptLoopAsync());
+
+ return Task.CompletedTask;
+ }
+
+ public async Task UnbindAsync()
+ {
+ if (_listenSocket != null)
+ {
+ var listenSocket = _listenSocket;
+ _listenSocket = null;
+
+ listenSocket.Dispose();
+
+ Debug.Assert(_listenTask != null);
+ await _listenTask;
+ _listenTask = null;
+ }
+ }
+
+ public Task StopAsync()
+ {
+ return Task.CompletedTask;
+ }
+
+ private async Task RunAcceptLoopAsync()
+ {
+ try
+ {
+ while (true)
+ {
+ Socket acceptSocket = await _listenSocket.AcceptAsync();
+
+ acceptSocket.NoDelay = _endPointInformation.NoDelay;
+
+ SocketConnection connection = new SocketConnection(acceptSocket, this);
+ connection.Start(_handler);
+ }
+ }
+ catch (Exception)
+ {
+ if (_listenSocket == null)
+ {
+ // Means we must be unbinding. Eat the exception.
+ }
+ else
+ {
+ throw;
+ }
+ }
+ }
+
+ internal SocketTransportFactory TransportFactory => _transportFactory;
+ }
+}
diff --git a/src/Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets/SocketTransportFactory.cs b/src/Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets/SocketTransportFactory.cs
new file mode 100644
index 0000000000..48f15ffc35
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets/SocketTransportFactory.cs
@@ -0,0 +1,44 @@
+// 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.Server.Kestrel.Internal.System.IO.Pipelines;
+using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions;
+using System;
+
+namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets
+{
+ public sealed class SocketTransportFactory : ITransportFactory
+ {
+ private readonly PipeFactory _pipeFactory = new PipeFactory();
+ private readonly bool _forceDispatch;
+
+ public SocketTransportFactory(bool forceDispatch = false)
+ {
+ _forceDispatch = forceDispatch;
+ }
+
+ public ITransport Create(IEndPointInformation endPointInformation, IConnectionHandler handler)
+ {
+ if (endPointInformation == null)
+ {
+ throw new ArgumentNullException(nameof(endPointInformation));
+ }
+
+ if (endPointInformation.Type != ListenType.IPEndPoint)
+ {
+ throw new ArgumentException("Only ListenType.IPEndPoint is supported", nameof(endPointInformation));
+ }
+
+ if (handler == null)
+ {
+ throw new ArgumentNullException(nameof(handler));
+ }
+
+ return new SocketTransport(this, endPointInformation, handler);
+ }
+
+ internal PipeFactory PipeFactory => _pipeFactory;
+
+ internal bool ForceDispatch => _forceDispatch;
+ }
+}
diff --git a/src/Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets/WebHostBuilderSocketExtensions.cs b/src/Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets/WebHostBuilderSocketExtensions.cs
new file mode 100644
index 0000000000..402a982c45
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets/WebHostBuilderSocketExtensions.cs
@@ -0,0 +1,29 @@
+// 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.Server.Kestrel.Transport.Abstractions;
+using Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace Microsoft.AspNetCore.Hosting
+{
+ public static class WebHostBuilderSocketExtensions
+ {
+ ///
+ /// Specify Sockets as the transport to be used by Kestrel.
+ ///
+ ///
+ /// The Microsoft.AspNetCore.Hosting.IWebHostBuilder to configure.
+ ///
+ ///
+ /// The Microsoft.AspNetCore.Hosting.IWebHostBuilder.
+ ///
+ public static IWebHostBuilder UseSockets(this IWebHostBuilder hostBuilder)
+ {
+ return hostBuilder.ConfigureServices(services =>
+ {
+ services.AddSingleton();
+ });
+ }
+ }
+}
diff --git a/src/Microsoft.AspNetCore.Server.Kestrel/Microsoft.AspNetCore.Server.Kestrel.csproj b/src/Microsoft.AspNetCore.Server.Kestrel/Microsoft.AspNetCore.Server.Kestrel.csproj
index 91e491c4f9..8adf6b216c 100644
--- a/src/Microsoft.AspNetCore.Server.Kestrel/Microsoft.AspNetCore.Server.Kestrel.csproj
+++ b/src/Microsoft.AspNetCore.Server.Kestrel/Microsoft.AspNetCore.Server.Kestrel.csproj
@@ -18,6 +18,7 @@
+
diff --git a/test/Microsoft.AspNetCore.Server.Kestrel.Core.Tests/FrameTests.cs b/test/Microsoft.AspNetCore.Server.Kestrel.Core.Tests/FrameTests.cs
index 33646df6ed..f2928ac9c2 100644
--- a/test/Microsoft.AspNetCore.Server.Kestrel.Core.Tests/FrameTests.cs
+++ b/test/Microsoft.AspNetCore.Server.Kestrel.Core.Tests/FrameTests.cs
@@ -813,6 +813,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
public IPEndPoint RemoteEndPoint { get; }
public IPEndPoint LocalEndPoint { get; }
public PipeFactory PipeFactory { get; }
+ public bool RequiresDispatch { get; }
public IScheduler InputWriterScheduler { get; }
public IScheduler OutputReaderScheduler { get; }
}
diff --git a/test/Microsoft.AspNetCore.Server.Kestrel.Core.Tests/PipeOptionsTests.cs b/test/Microsoft.AspNetCore.Server.Kestrel.Core.Tests/PipeOptionsTests.cs
index 6183e278fe..b8fe2bb78b 100644
--- a/test/Microsoft.AspNetCore.Server.Kestrel.Core.Tests/PipeOptionsTests.cs
+++ b/test/Microsoft.AspNetCore.Server.Kestrel.Core.Tests/PipeOptionsTests.cs
@@ -24,7 +24,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
var connectionHandler = new ConnectionHandler