Justin Kotalik 6 лет назад
Родитель
Сommit
845e6d5512
35 измененных файлов с 2115 добавлено и 82 удалено
  1. 1 0
      .gitignore
  2. 15 0
      src/Servers/Connections.Abstractions/ref/Microsoft.AspNetCore.Connections.Abstractions.netcoreapp.cs
  3. 15 0
      src/Servers/Connections.Abstractions/ref/Microsoft.AspNetCore.Connections.Abstractions.netstandard2.0.cs
  4. 15 0
      src/Servers/Connections.Abstractions/ref/Microsoft.AspNetCore.Connections.Abstractions.netstandard2.1.cs
  5. 13 0
      src/Servers/Connections.Abstractions/src/Features/IQuicCreateStreamFeature.cs
  6. 12 0
      src/Servers/Connections.Abstractions/src/Features/IQuicStreamListenerFeature.cs
  7. 9 0
      src/Servers/Connections.Abstractions/src/Features/IUnidirectionalStreamFeature.cs
  8. 44 14
      src/Servers/Kestrel/Kestrel.sln
  9. 35 0
      src/Servers/Kestrel/Transport.MsQuic/ref/Microsoft.AspNetCore.Server.Kestrel.Transport.MsQuic.netcoreapp.cs
  10. 16 0
      src/Servers/Kestrel/Transport.MsQuic/src/Internal/IMsQuicTrace.cs
  11. 51 0
      src/Servers/Kestrel/Transport.MsQuic/src/Internal/MsQuicApi.cs
  12. 334 0
      src/Servers/Kestrel/Transport.MsQuic/src/Internal/MsQuicConnection.cs
  13. 195 0
      src/Servers/Kestrel/Transport.MsQuic/src/Internal/MsQuicConnectionListener.cs
  14. 6 3
      src/Servers/Kestrel/Transport.MsQuic/src/Internal/MsQuicConstants.cs
  15. 104 57
      src/Servers/Kestrel/Transport.MsQuic/src/Internal/MsQuicNativeMethods.cs
  16. 443 0
      src/Servers/Kestrel/Transport.MsQuic/src/Internal/MsQuicStream.cs
  17. 53 0
      src/Servers/Kestrel/Transport.MsQuic/src/Internal/MsQuicTrace.cs
  18. 21 0
      src/Servers/Kestrel/Transport.MsQuic/src/Internal/MsQuicTransportContext.cs
  19. 44 0
      src/Servers/Kestrel/Transport.MsQuic/src/Internal/QuicSecConfig.cs
  20. 141 0
      src/Servers/Kestrel/Transport.MsQuic/src/Internal/QuicSession.cs
  21. 58 0
      src/Servers/Kestrel/Transport.MsQuic/src/Internal/ResettableCompletionSource.cs
  22. 25 0
      src/Servers/Kestrel/Transport.MsQuic/src/Internal/UIntExtensions.cs
  23. 24 1
      src/Servers/Kestrel/Transport.MsQuic/src/Microsoft.AspNetCore.Server.Kestrel.Transport.MsQuic.csproj
  24. 64 0
      src/Servers/Kestrel/Transport.MsQuic/src/MsQuicConnectionFactory.cs
  25. 47 0
      src/Servers/Kestrel/Transport.MsQuic/src/MsQuicTransportFactory.cs
  26. 55 0
      src/Servers/Kestrel/Transport.MsQuic/src/MsQuicTransportOptions.cs
  27. 30 0
      src/Servers/Kestrel/Transport.MsQuic/src/WebHostBuilderMsQuicExtensions.cs
  28. 104 0
      src/Servers/Kestrel/samples/QuicSampleApp/Program.cs
  29. 16 0
      src/Servers/Kestrel/samples/QuicSampleApp/QuicSampleApp.csproj
  30. 106 0
      src/Servers/Kestrel/samples/QuicSampleClient/Program.cs
  31. 15 0
      src/Servers/Kestrel/samples/QuicSampleClient/QuicSampleClient.csproj
  32. 1 1
      src/Servers/Kestrel/shared/DuplexPipe.cs
  33. 1 2
      src/Tools/dotnet-watch/test/DotNetWatcherTests.cs
  34. 1 2
      src/Tools/dotnet-watch/test/GlobbingAppTests.cs
  35. 1 2
      src/Tools/dotnet-watch/test/NoDepsAppTests.cs

+ 1 - 0
.gitignore

@@ -29,6 +29,7 @@ modules/
 *.psess
 *.res
 *.snk
+*.so
 *.suo
 *.tlog
 *.user

+ 15 - 0
src/Servers/Connections.Abstractions/ref/Microsoft.AspNetCore.Connections.Abstractions.netcoreapp.cs

@@ -136,6 +136,14 @@ namespace Microsoft.AspNetCore.Connections
         public override string ToString() { throw null; }
     }
 }
+namespace Microsoft.AspNetCore.Connections.Abstractions.Features
+{
+    public partial interface IQuicCreateStreamFeature
+    {
+        System.Threading.Tasks.ValueTask<Microsoft.AspNetCore.Connections.ConnectionContext> StartBidirectionalStreamAsync();
+        System.Threading.Tasks.ValueTask<Microsoft.AspNetCore.Connections.ConnectionContext> StartUnidirectionalStreamAsync();
+    }
+}
 namespace Microsoft.AspNetCore.Connections.Features
 {
     public partial interface IConnectionCompleteFeature
@@ -185,6 +193,10 @@ namespace Microsoft.AspNetCore.Connections.Features
     {
         System.Buffers.MemoryPool<byte> MemoryPool { get; }
     }
+    public partial interface IQuicStreamListenerFeature
+    {
+        System.Threading.Tasks.ValueTask<Microsoft.AspNetCore.Connections.ConnectionContext> AcceptAsync();
+    }
     public partial interface ITlsHandshakeFeature
     {
         System.Security.Authentication.CipherAlgorithmType CipherAlgorithm { get; }
@@ -200,4 +212,7 @@ namespace Microsoft.AspNetCore.Connections.Features
         Microsoft.AspNetCore.Connections.TransferFormat ActiveFormat { get; set; }
         Microsoft.AspNetCore.Connections.TransferFormat SupportedFormats { get; }
     }
+    public partial interface IUnidirectionalStreamFeature
+    {
+    }
 }

+ 15 - 0
src/Servers/Connections.Abstractions/ref/Microsoft.AspNetCore.Connections.Abstractions.netstandard2.0.cs

@@ -136,6 +136,14 @@ namespace Microsoft.AspNetCore.Connections
         public override string ToString() { throw null; }
     }
 }
+namespace Microsoft.AspNetCore.Connections.Abstractions.Features
+{
+    public partial interface IQuicCreateStreamFeature
+    {
+        System.Threading.Tasks.ValueTask<Microsoft.AspNetCore.Connections.ConnectionContext> StartBidirectionalStreamAsync();
+        System.Threading.Tasks.ValueTask<Microsoft.AspNetCore.Connections.ConnectionContext> StartUnidirectionalStreamAsync();
+    }
+}
 namespace Microsoft.AspNetCore.Connections.Features
 {
     public partial interface IConnectionCompleteFeature
@@ -185,6 +193,10 @@ namespace Microsoft.AspNetCore.Connections.Features
     {
         System.Buffers.MemoryPool<byte> MemoryPool { get; }
     }
+    public partial interface IQuicStreamListenerFeature
+    {
+        System.Threading.Tasks.ValueTask<Microsoft.AspNetCore.Connections.ConnectionContext> AcceptAsync();
+    }
     public partial interface ITlsHandshakeFeature
     {
         System.Security.Authentication.CipherAlgorithmType CipherAlgorithm { get; }
@@ -200,4 +212,7 @@ namespace Microsoft.AspNetCore.Connections.Features
         Microsoft.AspNetCore.Connections.TransferFormat ActiveFormat { get; set; }
         Microsoft.AspNetCore.Connections.TransferFormat SupportedFormats { get; }
     }
+    public partial interface IUnidirectionalStreamFeature
+    {
+    }
 }

+ 15 - 0
src/Servers/Connections.Abstractions/ref/Microsoft.AspNetCore.Connections.Abstractions.netstandard2.1.cs

@@ -136,6 +136,14 @@ namespace Microsoft.AspNetCore.Connections
         public override string ToString() { throw null; }
     }
 }
+namespace Microsoft.AspNetCore.Connections.Abstractions.Features
+{
+    public partial interface IQuicCreateStreamFeature
+    {
+        System.Threading.Tasks.ValueTask<Microsoft.AspNetCore.Connections.ConnectionContext> StartBidirectionalStreamAsync();
+        System.Threading.Tasks.ValueTask<Microsoft.AspNetCore.Connections.ConnectionContext> StartUnidirectionalStreamAsync();
+    }
+}
 namespace Microsoft.AspNetCore.Connections.Features
 {
     public partial interface IConnectionCompleteFeature
@@ -185,6 +193,10 @@ namespace Microsoft.AspNetCore.Connections.Features
     {
         System.Buffers.MemoryPool<byte> MemoryPool { get; }
     }
+    public partial interface IQuicStreamListenerFeature
+    {
+        System.Threading.Tasks.ValueTask<Microsoft.AspNetCore.Connections.ConnectionContext> AcceptAsync();
+    }
     public partial interface ITlsHandshakeFeature
     {
         System.Security.Authentication.CipherAlgorithmType CipherAlgorithm { get; }
@@ -200,4 +212,7 @@ namespace Microsoft.AspNetCore.Connections.Features
         Microsoft.AspNetCore.Connections.TransferFormat ActiveFormat { get; set; }
         Microsoft.AspNetCore.Connections.TransferFormat SupportedFormats { get; }
     }
+    public partial interface IUnidirectionalStreamFeature
+    {
+    }
 }

+ 13 - 0
src/Servers/Connections.Abstractions/src/Features/IQuicCreateStreamFeature.cs

@@ -0,0 +1,13 @@
+// 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.Threading.Tasks;
+
+namespace Microsoft.AspNetCore.Connections.Abstractions.Features
+{
+    public interface IQuicCreateStreamFeature
+    {
+        ValueTask<ConnectionContext> StartUnidirectionalStreamAsync();
+        ValueTask<ConnectionContext> StartBidirectionalStreamAsync();
+    }
+}

+ 12 - 0
src/Servers/Connections.Abstractions/src/Features/IQuicStreamListenerFeature.cs

@@ -0,0 +1,12 @@
+// 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.Threading.Tasks;
+
+namespace Microsoft.AspNetCore.Connections.Features
+{
+    public interface IQuicStreamListenerFeature
+    {
+        ValueTask<ConnectionContext> AcceptAsync();
+    }
+}

+ 9 - 0
src/Servers/Connections.Abstractions/src/Features/IUnidirectionalStreamFeature.cs

@@ -0,0 +1,9 @@
+// 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.
+
+namespace Microsoft.AspNetCore.Connections.Features
+{
+    public interface IUnidirectionalStreamFeature
+    {
+    }
+}

+ 44 - 14
src/Servers/Kestrel/Kestrel.sln

@@ -84,7 +84,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.WebUti
 EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "http2cat", "samples\http2cat\http2cat.csproj", "{3D6821F5-F242-4828-8DDE-89488E85512D}"
 EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Server.Kestrel.Transport.MsQuic", "Transport.MsQuic\src\Microsoft.AspNetCore.Server.Kestrel.Transport.MsQuic.csproj", "{1BC94F37-AF61-4641-A80A-EC32A15C5344}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "QuicSampleApp", "samples\QuicSampleApp\QuicSampleApp.csproj", "{53A8634C-DFC5-4A5B-8864-9EF1707E3F18}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Server.Kestrel.Transport.MsQuic", "Transport.MsQuic\src\Microsoft.AspNetCore.Server.Kestrel.Transport.MsQuic.csproj", "{62CFF861-807E-43F6-9403-22AA7F06C9A6}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "QuicSampleClient", "samples\QuicSampleClient\QuicSampleClient.csproj", "{F39A942B-85A8-4C1B-A5BC-435555E79F20}"
 EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -468,18 +472,42 @@ Global
 		{3D6821F5-F242-4828-8DDE-89488E85512D}.Release|x64.Build.0 = Release|Any CPU
 		{3D6821F5-F242-4828-8DDE-89488E85512D}.Release|x86.ActiveCfg = Release|Any CPU
 		{3D6821F5-F242-4828-8DDE-89488E85512D}.Release|x86.Build.0 = Release|Any CPU
-		{1BC94F37-AF61-4641-A80A-EC32A15C5344}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{1BC94F37-AF61-4641-A80A-EC32A15C5344}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{1BC94F37-AF61-4641-A80A-EC32A15C5344}.Debug|x64.ActiveCfg = Debug|Any CPU
-		{1BC94F37-AF61-4641-A80A-EC32A15C5344}.Debug|x64.Build.0 = Debug|Any CPU
-		{1BC94F37-AF61-4641-A80A-EC32A15C5344}.Debug|x86.ActiveCfg = Debug|Any CPU
-		{1BC94F37-AF61-4641-A80A-EC32A15C5344}.Debug|x86.Build.0 = Debug|Any CPU
-		{1BC94F37-AF61-4641-A80A-EC32A15C5344}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{1BC94F37-AF61-4641-A80A-EC32A15C5344}.Release|Any CPU.Build.0 = Release|Any CPU
-		{1BC94F37-AF61-4641-A80A-EC32A15C5344}.Release|x64.ActiveCfg = Release|Any CPU
-		{1BC94F37-AF61-4641-A80A-EC32A15C5344}.Release|x64.Build.0 = Release|Any CPU
-		{1BC94F37-AF61-4641-A80A-EC32A15C5344}.Release|x86.ActiveCfg = Release|Any CPU
-		{1BC94F37-AF61-4641-A80A-EC32A15C5344}.Release|x86.Build.0 = Release|Any CPU
+		{53A8634C-DFC5-4A5B-8864-9EF1707E3F18}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{53A8634C-DFC5-4A5B-8864-9EF1707E3F18}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{53A8634C-DFC5-4A5B-8864-9EF1707E3F18}.Debug|x64.ActiveCfg = Debug|Any CPU
+		{53A8634C-DFC5-4A5B-8864-9EF1707E3F18}.Debug|x64.Build.0 = Debug|Any CPU
+		{53A8634C-DFC5-4A5B-8864-9EF1707E3F18}.Debug|x86.ActiveCfg = Debug|Any CPU
+		{53A8634C-DFC5-4A5B-8864-9EF1707E3F18}.Debug|x86.Build.0 = Debug|Any CPU
+		{53A8634C-DFC5-4A5B-8864-9EF1707E3F18}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{53A8634C-DFC5-4A5B-8864-9EF1707E3F18}.Release|Any CPU.Build.0 = Release|Any CPU
+		{53A8634C-DFC5-4A5B-8864-9EF1707E3F18}.Release|x64.ActiveCfg = Release|Any CPU
+		{53A8634C-DFC5-4A5B-8864-9EF1707E3F18}.Release|x64.Build.0 = Release|Any CPU
+		{53A8634C-DFC5-4A5B-8864-9EF1707E3F18}.Release|x86.ActiveCfg = Release|Any CPU
+		{53A8634C-DFC5-4A5B-8864-9EF1707E3F18}.Release|x86.Build.0 = Release|Any CPU
+		{62CFF861-807E-43F6-9403-22AA7F06C9A6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{62CFF861-807E-43F6-9403-22AA7F06C9A6}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{62CFF861-807E-43F6-9403-22AA7F06C9A6}.Debug|x64.ActiveCfg = Debug|Any CPU
+		{62CFF861-807E-43F6-9403-22AA7F06C9A6}.Debug|x64.Build.0 = Debug|Any CPU
+		{62CFF861-807E-43F6-9403-22AA7F06C9A6}.Debug|x86.ActiveCfg = Debug|Any CPU
+		{62CFF861-807E-43F6-9403-22AA7F06C9A6}.Debug|x86.Build.0 = Debug|Any CPU
+		{62CFF861-807E-43F6-9403-22AA7F06C9A6}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{62CFF861-807E-43F6-9403-22AA7F06C9A6}.Release|Any CPU.Build.0 = Release|Any CPU
+		{62CFF861-807E-43F6-9403-22AA7F06C9A6}.Release|x64.ActiveCfg = Release|Any CPU
+		{62CFF861-807E-43F6-9403-22AA7F06C9A6}.Release|x64.Build.0 = Release|Any CPU
+		{62CFF861-807E-43F6-9403-22AA7F06C9A6}.Release|x86.ActiveCfg = Release|Any CPU
+		{62CFF861-807E-43F6-9403-22AA7F06C9A6}.Release|x86.Build.0 = Release|Any CPU
+		{F39A942B-85A8-4C1B-A5BC-435555E79F20}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{F39A942B-85A8-4C1B-A5BC-435555E79F20}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{F39A942B-85A8-4C1B-A5BC-435555E79F20}.Debug|x64.ActiveCfg = Debug|Any CPU
+		{F39A942B-85A8-4C1B-A5BC-435555E79F20}.Debug|x64.Build.0 = Debug|Any CPU
+		{F39A942B-85A8-4C1B-A5BC-435555E79F20}.Debug|x86.ActiveCfg = Debug|Any CPU
+		{F39A942B-85A8-4C1B-A5BC-435555E79F20}.Debug|x86.Build.0 = Debug|Any CPU
+		{F39A942B-85A8-4C1B-A5BC-435555E79F20}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{F39A942B-85A8-4C1B-A5BC-435555E79F20}.Release|Any CPU.Build.0 = Release|Any CPU
+		{F39A942B-85A8-4C1B-A5BC-435555E79F20}.Release|x64.ActiveCfg = Release|Any CPU
+		{F39A942B-85A8-4C1B-A5BC-435555E79F20}.Release|x64.Build.0 = Release|Any CPU
+		{F39A942B-85A8-4C1B-A5BC-435555E79F20}.Release|x86.ActiveCfg = Release|Any CPU
+		{F39A942B-85A8-4C1B-A5BC-435555E79F20}.Release|x86.Build.0 = Release|Any CPU
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE
@@ -517,7 +545,9 @@ Global
 		{E0AD50A3-2518-4060-8BB9-5649B04B3A6D} = {F0A1281A-B512-49D2-8362-21EE32B3674F}
 		{EE45763C-753D-4228-8E5D-A71F8BDB3D89} = {F0A1281A-B512-49D2-8362-21EE32B3674F}
 		{3D6821F5-F242-4828-8DDE-89488E85512D} = {F826BA61-60A9-45B6-AF29-FD1A6E313EF0}
-		{1BC94F37-AF61-4641-A80A-EC32A15C5344} = {2B456D08-F72B-4EB8-B663-B6D78FC04BF8}
+		{53A8634C-DFC5-4A5B-8864-9EF1707E3F18} = {F826BA61-60A9-45B6-AF29-FD1A6E313EF0}
+		{62CFF861-807E-43F6-9403-22AA7F06C9A6} = {2B456D08-F72B-4EB8-B663-B6D78FC04BF8}
+		{F39A942B-85A8-4C1B-A5BC-435555E79F20} = {F826BA61-60A9-45B6-AF29-FD1A6E313EF0}
 	EndGlobalSection
 	GlobalSection(ExtensibilityGlobals) = postSolution
 		SolutionGuid = {48207B50-7D05-4B10-B585-890FE0F4FCE1}

+ 35 - 0
src/Servers/Kestrel/Transport.MsQuic/ref/Microsoft.AspNetCore.Server.Kestrel.Transport.MsQuic.netcoreapp.cs

@@ -1,3 +1,38 @@
 // 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.
 
+namespace Microsoft.AspNetCore.Hosting
+{
+    public static partial class WebHostBuilderMsQuicExtensions
+    {
+        public static Microsoft.AspNetCore.Hosting.IWebHostBuilder UseMsQuic(this Microsoft.AspNetCore.Hosting.IWebHostBuilder hostBuilder) { throw null; }
+        public static Microsoft.AspNetCore.Hosting.IWebHostBuilder UseMsQuic(this Microsoft.AspNetCore.Hosting.IWebHostBuilder hostBuilder, System.Action<Microsoft.AspNetCore.Server.Kestrel.Transport.MsQuic.MsQuicTransportOptions> configureOptions) { throw null; }
+    }
+}
+namespace Microsoft.AspNetCore.Server.Kestrel.Transport.MsQuic
+{
+    public partial class MsQuicConnectionFactory : Microsoft.AspNetCore.Connections.IConnectionFactory
+    {
+        public MsQuicConnectionFactory(Microsoft.Extensions.Options.IOptions<Microsoft.AspNetCore.Server.Kestrel.Transport.MsQuic.MsQuicTransportOptions> options, Microsoft.Extensions.Hosting.IHostApplicationLifetime lifetime, Microsoft.Extensions.Logging.ILoggerFactory loggerFactory) { }
+        [System.Diagnostics.DebuggerStepThroughAttribute]
+        public System.Threading.Tasks.ValueTask<Microsoft.AspNetCore.Connections.ConnectionContext> ConnectAsync(System.Net.EndPoint endPoint, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
+    }
+    public partial class MsQuicTransportFactory : Microsoft.AspNetCore.Connections.IConnectionListenerFactory
+    {
+        public MsQuicTransportFactory(Microsoft.Extensions.Hosting.IHostApplicationLifetime applicationLifetime, Microsoft.Extensions.Logging.ILoggerFactory loggerFactory, Microsoft.Extensions.Options.IOptions<Microsoft.AspNetCore.Server.Kestrel.Transport.MsQuic.MsQuicTransportOptions> options) { }
+        [System.Diagnostics.DebuggerStepThroughAttribute]
+        public System.Threading.Tasks.ValueTask<Microsoft.AspNetCore.Connections.IConnectionListener> BindAsync(System.Net.EndPoint endpoint, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
+    }
+    public partial class MsQuicTransportOptions
+    {
+        public MsQuicTransportOptions() { }
+        public string Alpn { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } }
+        public System.Security.Cryptography.X509Certificates.X509Certificate2 Certificate { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } }
+        public System.TimeSpan IdleTimeout { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } }
+        public ushort MaxBidirectionalStreamCount { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } }
+        public long? MaxReadBufferSize { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } }
+        public ushort MaxUnidirectionalStreamCount { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } }
+        public long? MaxWriteBufferSize { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } }
+        public string RegistrationName { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } }
+    }
+}

+ 16 - 0
src/Servers/Kestrel/Transport.MsQuic/src/Internal/IMsQuicTrace.cs

@@ -0,0 +1,16 @@
+// 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.MsQuic.Internal
+{
+    internal interface IMsQuicTrace : ILogger
+    {
+        void NewConnection(string connectionId);
+        void NewStream(string streamId);
+        void ConnectionError(string connectionId, Exception ex);
+        void StreamError(string streamId, Exception ex);
+    }
+}

+ 51 - 0
src/Servers/Kestrel/Transport.MsQuic/src/Internal/MsQuicApi.cs

@@ -3,6 +3,9 @@
 
 using System;
 using System.Runtime.InteropServices;
+using System.Security.Cryptography.X509Certificates;
+using System.Text;
+using System.Threading.Tasks;
 
 namespace Microsoft.AspNetCore.Server.Kestrel.Transport.MsQuic.Internal
 {
@@ -160,6 +163,54 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.MsQuic.Internal
                 Buffer.Buffer);
         }
 
+        public async ValueTask<QuicSecConfig> CreateSecurityConfig(X509Certificate2 certificate)
+        {
+            QuicSecConfig secConfig = null;
+            var tcs = new TaskCompletionSource<object>();
+            var secConfigCreateStatus = MsQuicConstants.InternalError;
+
+            var status = SecConfigCreateDelegate(
+                _registrationContext,
+                (uint)QUIC_SEC_CONFIG_FLAG.CERT_CONTEXT,
+                certificate.Handle,
+                null,
+                IntPtr.Zero,
+                SecCfgCreateCallbackHandler);
+
+            MsQuicStatusException.ThrowIfFailed(status);
+
+            void SecCfgCreateCallbackHandler(
+                IntPtr context,
+                uint status,
+                IntPtr securityConfig)
+            {
+                secConfig = new QuicSecConfig(this, securityConfig);
+                secConfigCreateStatus = status;
+                tcs.SetResult(null);
+            }
+
+            await tcs.Task;
+
+            MsQuicStatusException.ThrowIfFailed(secConfigCreateStatus);
+
+            return secConfig;
+        }
+
+        public QuicSession SessionOpen(
+           string alpn)
+        {
+            var sessionPtr = IntPtr.Zero;
+
+            var status = SessionOpenDelegate(
+                _registrationContext,
+                Encoding.UTF8.GetBytes(alpn),
+                IntPtr.Zero,
+                ref sessionPtr);
+            MsQuicStatusException.ThrowIfFailed(status);
+
+            return new QuicSession(this, sessionPtr);
+        }
+
         public void Dispose()
         {
             Dispose(disposing: true);

+ 334 - 0
src/Servers/Kestrel/Transport.MsQuic/src/Internal/MsQuicConnection.cs

@@ -0,0 +1,334 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Runtime.InteropServices;
+using System.Threading.Channels;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Connections;
+using Microsoft.AspNetCore.Connections.Abstractions.Features;
+using Microsoft.AspNetCore.Connections.Features;
+using static Microsoft.AspNetCore.Server.Kestrel.Transport.MsQuic.Internal.MsQuicNativeMethods;
+
+namespace Microsoft.AspNetCore.Server.Kestrel.Transport.MsQuic.Internal
+{
+    internal class MsQuicConnection : TransportConnection, IQuicStreamListenerFeature, IQuicCreateStreamFeature, IDisposable
+    {
+        public MsQuicApi _api;
+        private bool _disposed;
+        private readonly MsQuicTransportContext _context;
+        private readonly IMsQuicTrace _log;
+        private IntPtr _nativeObjPtr;
+        private static GCHandle _handle;
+        private ConnectionCallbackDelegate _connectionDelegate;
+        private readonly Channel<MsQuicStream> _acceptQueue = Channel.CreateUnbounded<MsQuicStream>(new UnboundedChannelOptions
+        {
+            SingleReader = true,
+            SingleWriter = true
+        });
+
+        public MsQuicConnection(MsQuicApi api, MsQuicTransportContext context, IntPtr nativeObjPtr)
+        {
+            _api = api;
+            _context = context;
+            _log = context.Log;
+            _nativeObjPtr = nativeObjPtr;
+
+            SetCallbackHandler();
+
+            SetIdleTimeout(_context.Options.IdleTimeout);
+
+            Features.Set<IQuicStreamListenerFeature>(this);
+            Features.Set<IQuicCreateStreamFeature>(this);
+
+            _log.NewConnection(ConnectionId);
+        }
+
+        internal uint HandleEvent(ref ConnectionEvent connectionEvent)
+        {
+            var status = MsQuicConstants.Success;
+            switch (connectionEvent.Type)
+            {
+                case QUIC_CONNECTION_EVENT.CONNECTED:
+                    {
+                        status = HandleEventConnected(
+                            connectionEvent);
+                    }
+                    break;
+
+                case QUIC_CONNECTION_EVENT.SHUTDOWN_BEGIN:
+                    {
+                        status = HandleEventShutdownBegin(
+                            connectionEvent);
+                    }
+                    break;
+
+                case QUIC_CONNECTION_EVENT.SHUTDOWN_BEGIN_PEER:
+                    {
+                        status = HandleEventShutdownBeginPeer(
+                            connectionEvent);
+                    }
+                    break;
+
+                case QUIC_CONNECTION_EVENT.SHUTDOWN_COMPLETE:
+                    {
+                        status = HandleEventShutdownComplete(
+                            connectionEvent);
+                    }
+                    break;
+
+                case QUIC_CONNECTION_EVENT.NEW_STREAM:
+                    {
+                        status = HandleEventNewStream(
+                            connectionEvent);
+                    }
+                    break;
+
+                case QUIC_CONNECTION_EVENT.STREAMS_AVAILABLE:
+                    {
+                        status = HandleEventStreamsAvailable(
+                            connectionEvent);
+                    }
+                    break;
+
+                default:
+                    break;
+            }
+            return status;
+        }
+
+        protected virtual uint HandleEventConnected(ConnectionEvent connectionEvent)
+        {
+            return MsQuicConstants.Success;
+        }
+
+        protected virtual uint HandleEventShutdownBegin(ConnectionEvent connectionEvent)
+        {
+            return MsQuicConstants.Success;
+        }
+
+        protected virtual uint HandleEventShutdownBeginPeer(ConnectionEvent connectionEvent)
+        {
+            return MsQuicConstants.Success;
+        }
+
+        protected virtual uint HandleEventShutdownComplete(ConnectionEvent connectionEvent)
+        {
+            return MsQuicConstants.Success;
+        }
+
+        protected virtual uint HandleEventNewStream(ConnectionEvent connectionEvent)
+        {
+            var msQuicStream = new MsQuicStream(_api, this, _context, connectionEvent.StreamFlags, connectionEvent.Data.NewStream.Stream);
+
+            _acceptQueue.Writer.TryWrite(msQuicStream);
+
+            return MsQuicConstants.Success;
+        }
+
+        protected virtual uint HandleEventStreamsAvailable(ConnectionEvent connectionEvent)
+        {
+            return MsQuicConstants.Success;
+        }
+
+        public async ValueTask<ConnectionContext> AcceptAsync()
+        {
+            if (await _acceptQueue.Reader.WaitToReadAsync())
+            {
+                if (_acceptQueue.Reader.TryRead(out var stream))
+                {
+                    return stream;
+                }
+            }
+
+            return null;
+        }
+
+        public ValueTask<ConnectionContext> StartUnidirectionalStreamAsync()
+        {
+            return StartStreamAsync(QUIC_STREAM_OPEN_FLAG.UNIDIRECTIONAL);
+        }
+
+        public ValueTask<ConnectionContext> StartBidirectionalStreamAsync()
+        {
+            return StartStreamAsync(QUIC_STREAM_OPEN_FLAG.NONE);
+        }
+
+        private async ValueTask<ConnectionContext> StartStreamAsync(QUIC_STREAM_OPEN_FLAG flags)
+        {
+            var stream = StreamOpen(flags);
+            await stream.StartAsync();
+            return stream;
+        }
+
+        public unsafe void SetIdleTimeout(TimeSpan timeout)
+        {
+            var msTime = (ulong)timeout.TotalMilliseconds;
+            var buffer = new QuicBuffer()
+            {
+                Length = sizeof(ulong),
+                Buffer = (byte*)&msTime
+            };
+            SetParam(QUIC_PARAM_CONN.IDLE_TIMEOUT, buffer);
+        }
+
+        public void SetPeerBiDirectionalStreamCount(ushort count)
+        {
+            SetUshortParamter(QUIC_PARAM_CONN.PEER_BIDI_STREAM_COUNT, count);
+        }
+
+        public void SetPeerUnidirectionalStreamCount(ushort count)
+        {
+            SetUshortParamter(QUIC_PARAM_CONN.PEER_UNIDI_STREAM_COUNT, count);
+        }
+
+        public void SetLocalBidirectionalStreamCount(ushort count)
+        {
+            SetUshortParamter(QUIC_PARAM_CONN.LOCAL_BIDI_STREAM_COUNT, count);
+        }
+
+        public void SetLocalUnidirectionalStreamCount(ushort count)
+        {
+            SetUshortParamter(QUIC_PARAM_CONN.LOCAL_UNIDI_STREAM_COUNT, count);
+        }
+
+        public unsafe void EnableBuffering()
+        {
+            var val = true;
+            var buffer = new QuicBuffer()
+            {
+                Length = sizeof(bool),
+                Buffer = (byte*)&val
+            };
+            SetParam(QUIC_PARAM_CONN.USE_SEND_BUFFER, buffer);
+        }
+
+        public unsafe void DisableBuffering()
+        {
+            var val = false;
+            var buffer = new QuicBuffer()
+            {
+                Length = sizeof(bool),
+                Buffer = (byte*)&val
+            };
+            SetParam(QUIC_PARAM_CONN.USE_SEND_BUFFER, buffer);
+        }
+
+        public ValueTask StartAsync(
+            ushort family,
+            string serverName,
+            ushort serverPort)
+        {
+            var status = _api.ConnectionStartDelegate(
+                _nativeObjPtr,
+                family,
+                serverName,
+                serverPort);
+
+            MsQuicStatusException.ThrowIfFailed(status);
+
+            return new ValueTask();
+        }
+
+        public MsQuicStream StreamOpen(
+            QUIC_STREAM_OPEN_FLAG flags)
+        {
+            var streamPtr = IntPtr.Zero;
+            var status = _api.StreamOpenDelegate(
+                _nativeObjPtr,
+                (uint)flags,
+                MsQuicStream.NativeCallbackHandler,
+                IntPtr.Zero,
+                out streamPtr);
+            MsQuicStatusException.ThrowIfFailed(status);
+
+            return new MsQuicStream(_api, this, _context, flags, streamPtr);
+        }
+
+        public void SetCallbackHandler()
+        {
+            _handle = GCHandle.Alloc(this);
+            _connectionDelegate = new ConnectionCallbackDelegate(NativeCallbackHandler);
+            _api.SetCallbackHandlerDelegate(
+                _nativeObjPtr,
+                _connectionDelegate,
+                GCHandle.ToIntPtr(_handle));
+        }
+
+        public void Shutdown(
+            QUIC_CONNECTION_SHUTDOWN_FLAG Flags,
+            ushort ErrorCode)
+        {
+            var status = _api.ConnectionShutdownDelegate(
+                _nativeObjPtr,
+                (uint)Flags,
+                ErrorCode);
+            MsQuicStatusException.ThrowIfFailed(status);
+        }
+
+        internal static uint NativeCallbackHandler(
+            IntPtr connection,
+            IntPtr context,
+            ref ConnectionEvent connectionEventStruct)
+        {
+            var handle = GCHandle.FromIntPtr(context);
+            var quicConnection = (MsQuicConnection)handle.Target;
+            return quicConnection.HandleEvent(ref connectionEventStruct);
+        }
+
+        public void Dispose()
+        {
+            Dispose(true);
+            GC.SuppressFinalize(this);
+        }
+
+        ~MsQuicConnection()
+        {
+            Dispose(false);
+        }
+
+        private void Dispose(bool disposing)
+        {
+            if (_disposed)
+            {
+                return;
+            }
+
+            if (_nativeObjPtr != IntPtr.Zero)
+            {
+                _api.ConnectionCloseDelegate?.Invoke(_nativeObjPtr);
+            }
+
+            _nativeObjPtr = IntPtr.Zero;
+            _api = null;
+
+            _handle.Free();
+            _disposed = true;
+        }
+
+        private unsafe void SetUshortParamter(QUIC_PARAM_CONN param, ushort count)
+        {
+            var buffer = new QuicBuffer()
+            {
+                Length = sizeof(ushort),
+                Buffer = (byte*)&count
+            };
+            SetParam(param, buffer);
+        }
+
+        private void SetParam(
+            QUIC_PARAM_CONN param,
+            QuicBuffer buf)
+        {
+            MsQuicStatusException.ThrowIfFailed(_api.UnsafeSetParam(
+                _nativeObjPtr,
+                (uint)QUIC_PARAM_LEVEL.CONNECTION,
+                (uint)param,
+                buf));
+        }
+
+        public override void Abort(ConnectionAbortedException abortReason)
+        {
+        }
+    }
+}

+ 195 - 0
src/Servers/Kestrel/Transport.MsQuic/src/Internal/MsQuicConnectionListener.cs

@@ -0,0 +1,195 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Collections.Generic;
+using System.Net;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Threading;
+using System.Threading.Channels;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Connections;
+using Microsoft.AspNetCore.Server.Kestrel.Transport.MsQuic.Internal;
+using Microsoft.Extensions.Hosting;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Options;
+using static Microsoft.AspNetCore.Server.Kestrel.Transport.MsQuic.Internal.MsQuicNativeMethods;
+
+namespace Microsoft.AspNetCore.Server.Kestrel.Transport.MsQuic.Internal
+{
+    /// <summary>
+    /// Listens for new Quic Connections.
+    /// </summary>
+    internal class MsQuicConnectionListener : IConnectionListener, IAsyncDisposable, IDisposable
+    {
+        private IMsQuicTrace _log;
+        private MsQuicApi _api;
+        private QuicSecConfig _secConfig;
+        private QuicSession _session;
+        private bool _disposed;
+        private bool _stopped;
+        private IntPtr _nativeObjPtr;
+        private GCHandle _handle;
+        private ListenerCallbackDelegate _listenerDelegate;
+        private MsQuicTransportContext _transportContext;
+
+        private readonly Channel<MsQuicConnection> _acceptConnectionQueue = Channel.CreateUnbounded<MsQuicConnection>(new UnboundedChannelOptions
+        {
+            SingleReader = true,
+            SingleWriter = true
+        });
+
+        public MsQuicConnectionListener(MsQuicTransportOptions options, IHostApplicationLifetime lifetime, IMsQuicTrace log, EndPoint endpoint)
+        {
+            _api = new MsQuicApi();
+            _log = log;
+            _transportContext = new MsQuicTransportContext(lifetime, _log, options);
+            EndPoint = endpoint;
+        }
+
+        public EndPoint EndPoint { get; set; }
+
+        public async ValueTask<ConnectionContext> AcceptAsync(CancellationToken cancellationToken = default)
+        {
+            if (await _acceptConnectionQueue.Reader.WaitToReadAsync())
+            {
+                if (_acceptConnectionQueue.Reader.TryRead(out var connection))
+                {
+                    return connection;
+                }
+            }
+
+            return null;
+        }
+
+        internal async Task BindAsync()
+        {
+            await StartAsync();
+        }
+
+        public async ValueTask UnbindAsync(CancellationToken cancellationToken = default)
+        {
+            if (_stopped)
+            {
+                return;
+            }
+
+            // TODO abort all streams and connections here?
+            _stopped = true;
+
+            await DisposeAsync();
+        }
+
+        public async Task StartAsync(CancellationToken cancellationToken = default)
+        {
+            _api.RegistrationOpen(Encoding.ASCII.GetBytes(_transportContext.Options.RegistrationName));
+
+            _secConfig = await _api.CreateSecurityConfig(_transportContext.Options.Certificate);
+
+            _session = _api.SessionOpen(_transportContext.Options.Alpn);
+            _log.LogDebug(0, "Started session");
+
+            _nativeObjPtr = _session.ListenerOpen(NativeCallbackHandler);
+
+            SetCallbackHandler();
+
+            _session.SetIdleTimeout(_transportContext.Options.IdleTimeout);
+            _session.SetPeerBiDirectionalStreamCount(_transportContext.Options.MaxBidirectionalStreamCount);
+            _session.SetPeerUnidirectionalStreamCount(_transportContext.Options.MaxBidirectionalStreamCount);
+
+            var address = MsQuicNativeMethods.Convert(EndPoint as IPEndPoint);
+            MsQuicStatusException.ThrowIfFailed(_api.ListenerStartDelegate(
+                _nativeObjPtr,
+                ref address));
+        }
+
+        internal uint ListenerCallbackHandler(
+            ref ListenerEvent evt)
+        {
+            switch (evt.Type)
+            {
+                case QUIC_LISTENER_EVENT.NEW_CONNECTION:
+                    {
+                        evt.Data.NewConnection.SecurityConfig = _secConfig.NativeObjPtr;
+                        var msQuicConnection = new MsQuicConnection(_api, _transportContext, evt.Data.NewConnection.Connection);
+                        _acceptConnectionQueue.Writer.TryWrite(msQuicConnection);
+                    }
+                    break;
+                default:
+                    return MsQuicConstants.InternalError;
+            }
+
+            return MsQuicConstants.Success;
+        }
+
+        protected void StopAcceptingConnections()
+        {
+            _acceptConnectionQueue.Writer.TryComplete();
+        }
+
+        internal static uint NativeCallbackHandler(
+            IntPtr listener,
+            IntPtr context,
+            ref ListenerEvent connectionEventStruct)
+        {
+            var handle = GCHandle.FromIntPtr(context);
+            var quicListener = (MsQuicConnectionListener)handle.Target;
+
+            return quicListener.ListenerCallbackHandler(ref connectionEventStruct);
+        }
+
+        internal void SetCallbackHandler()
+        {
+            _handle = GCHandle.Alloc(this);
+            _listenerDelegate = new ListenerCallbackDelegate(NativeCallbackHandler);
+            _api.SetCallbackHandlerDelegate(
+                _nativeObjPtr,
+                _listenerDelegate,
+                GCHandle.ToIntPtr(_handle));
+        }
+
+        ~MsQuicConnectionListener()
+        {
+            Dispose(false);
+        }
+
+        public void Dispose()
+        {
+            Dispose(true);
+            GC.SuppressFinalize(this);
+        }
+
+        public ValueTask DisposeAsync()
+        {
+            if (_disposed)
+            {
+                return new ValueTask();
+            }
+
+            Dispose(true);
+
+            return new ValueTask();
+        }
+
+        private void Dispose(bool disposing)
+        {
+            if (_disposed)
+            {
+                return;
+            }
+
+            StopAcceptingConnections();
+
+            if (_nativeObjPtr != IntPtr.Zero)
+            {
+                _api.ListenerStopDelegate(_nativeObjPtr);
+                _api.ListenerCloseDelegate(_nativeObjPtr);
+            }
+
+            _nativeObjPtr = IntPtr.Zero;
+            _api = null;
+            _disposed = true;
+        }
+    }
+}

+ 6 - 3
src/Servers/Kestrel/Transport.MsQuic/src/Internal/MsQuicConstants.cs

@@ -8,7 +8,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.MsQuic.Internal
 {
     internal static class MsQuicConstants
     {
-        private const uint Success = 0;
+        internal static uint InternalError = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? Windows.InternalError : Linux.InternalError;
+        internal static uint Success = 0;
+        internal static uint Pending = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? Windows.Pending : Linux.Pending;
+        private const uint SuccessConst = 0;
 
         internal static Func<uint, string> ErrorTypeFromErrorCode = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? Windows.GetError : (Func<uint, string>)Linux.GetError;
 
@@ -37,7 +40,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.MsQuic.Internal
             {
                 switch (status)
                 {
-                    case Success:
+                    case SuccessConst:
                         return "SUCCESS";
                     case Pending:
                         return "PENDING";
@@ -103,7 +106,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.MsQuic.Internal
             {
                 switch (status)
                 {
-                    case Success:
+                    case SuccessConst:
                         return "SUCCESS";
                     case Pending:
                         return "PENDING";

+ 104 - 57
src/Servers/Kestrel/Transport.MsQuic/src/Internal/MsQuicNativeMethods.cs

@@ -3,6 +3,7 @@
 
 using System;
 using System.Net;
+using System.Net.Sockets;
 using System.Runtime.InteropServices;
 using System.Text;
 
@@ -13,7 +14,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.MsQuic.Internal
     /// </summary>
     internal unsafe static class MsQuicNativeMethods
     {
-        internal const string dllName = "msquic.dll";
+        internal const string dllName = "msquic";
 
         [DllImport(dllName)]
         internal static extern int MsQuicOpen(int version, out NativeApi* registration);
@@ -67,7 +68,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.MsQuic.Internal
 
         internal delegate void SetCallbackHandlerDelegate(
             IntPtr Handle,
-            IntPtr Handler,
+            Delegate del,
             IntPtr Context);
 
         internal delegate uint SetParamDelegate(
@@ -413,61 +414,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.MsQuic.Internal
         {
             internal QUIC_STREAM_EVENT Type;
             internal StreamEventDataUnion Data;
-            internal uint ReceiveAbortError => Data.PeerRecvAbort.ErrorCode;
-            internal uint SendAbortError => Data.PeerSendAbort.ErrorCode;
-            internal ulong AbsoluteOffset => Data.Recv.AbsoluteOffset;
-            internal ulong TotalBufferLength => Data.Recv.TotalBufferLength;
-            internal void CopyToBuffer(Span<byte> buffer)
-            {
-                var length = (int)Data.Recv.Buffers[0].Length;
-                new Span<byte>(Data.Recv.Buffers[0].Buffer, length).CopyTo(buffer);
-            }
-            internal bool Canceled => Data.SendComplete.IsCanceled();
-            internal IntPtr ClientContext => Data.SendComplete.ClientContext;
-            internal bool GracefulShutdown => Data.SendShutdownComplete.Graceful;
-        }
-
-        internal delegate uint StreamCallbackDelegate(
-            IntPtr Stream,
-            IntPtr Context,
-            ref StreamEvent Event);
-
-        internal delegate uint StreamOpenDelegate(
-            IntPtr Connection,
-            uint Flags,
-            StreamCallbackDelegate Handler,
-            IntPtr Context,
-            out IntPtr Stream);
-
-        internal delegate uint StreamStartDelegate(
-            IntPtr Stream,
-            uint Flags
-            );
-
-        internal delegate uint StreamCloseDelegate(
-            IntPtr Stream);
-
-        internal delegate uint StreamShutdownDelegate(
-            IntPtr Stream,
-            uint Flags,
-            ushort ErrorCode);
-
-        internal delegate uint StreamSendDelegate(
-            IntPtr Stream,
-            QuicBuffer* Buffers,
-            uint BufferCount,
-            uint Flags,
-            IntPtr ClientSendContext);
-
-        internal delegate uint StreamReceiveCompleteDelegate(
-            IntPtr Stream,
-            ulong BufferLength);
-
-        [StructLayout(LayoutKind.Sequential)]
-        internal unsafe struct QuicBuffer
-        {
-            internal uint Length;
-            internal byte* Buffer;
         }
 
         [StructLayout(LayoutKind.Sequential)]
@@ -536,5 +482,106 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.MsQuic.Internal
             [FieldOffset(0)]
             internal ushort si_family;
         }
+
+        internal delegate uint StreamCallbackDelegate(
+            IntPtr Stream,
+            IntPtr Context,
+            StreamEvent Event);
+
+        internal delegate uint StreamOpenDelegate(
+            IntPtr Connection,
+            uint Flags,
+            StreamCallbackDelegate Handler,
+            IntPtr Context,
+            out IntPtr Stream);
+
+        internal delegate uint StreamStartDelegate(
+            IntPtr Stream,
+            uint Flags
+            );
+
+        internal delegate uint StreamCloseDelegate(
+            IntPtr Stream);
+
+        internal delegate uint StreamShutdownDelegate(
+            IntPtr Stream,
+            uint Flags,
+            ushort ErrorCode);
+
+        internal delegate uint StreamSendDelegate(
+            IntPtr Stream,
+            QuicBuffer* Buffers,
+            uint BufferCount,
+            uint Flags,
+            IntPtr ClientSendContext);
+
+        internal delegate uint StreamReceiveCompleteDelegate(
+            IntPtr Stream,
+            ulong BufferLength);
+
+        [StructLayout(LayoutKind.Sequential)]
+        internal unsafe struct QuicBuffer
+        {
+            internal uint Length;
+            internal byte* Buffer;
+        }
+
+        private const ushort IPv4 = 2;
+        private const ushort IPv6 = 23;
+        
+        public static SOCKADDR_INET Convert(IPEndPoint endpoint)
+        {
+            var socketAddress = new SOCKADDR_INET();
+            var buffer = endpoint.Address.GetAddressBytes();
+            switch (endpoint.Address.AddressFamily)
+            {
+                case AddressFamily.InterNetwork:
+                    socketAddress.Ipv4.sin_addr0 = buffer[0];
+                    socketAddress.Ipv4.sin_addr1 = buffer[1];
+                    socketAddress.Ipv4.sin_addr2 = buffer[2];
+                    socketAddress.Ipv4.sin_addr3 = buffer[3];
+                    socketAddress.Ipv4.sin_family = IPv4;
+                    break;
+                case AddressFamily.InterNetworkV6:
+                    socketAddress.Ipv6.sin6_addr0 = buffer[0];
+                    socketAddress.Ipv6.sin6_addr1 = buffer[1];
+                    socketAddress.Ipv6.sin6_addr2 = buffer[2];
+                    socketAddress.Ipv6.sin6_addr3 = buffer[3];
+                    socketAddress.Ipv6.sin6_addr4 = buffer[4];
+                    socketAddress.Ipv6.sin6_addr5 = buffer[5];
+                    socketAddress.Ipv6.sin6_addr6 = buffer[6];
+                    socketAddress.Ipv6.sin6_addr7 = buffer[7];
+                    socketAddress.Ipv6.sin6_addr8 = buffer[8];
+                    socketAddress.Ipv6.sin6_addr9 = buffer[9];
+                    socketAddress.Ipv6.sin6_addr10 = buffer[10];
+                    socketAddress.Ipv6.sin6_addr11 = buffer[11];
+                    socketAddress.Ipv6.sin6_addr12 = buffer[12];
+                    socketAddress.Ipv6.sin6_addr13 = buffer[13];
+                    socketAddress.Ipv6.sin6_addr14 = buffer[14];
+                    socketAddress.Ipv6.sin6_addr15 = buffer[15];
+                    socketAddress.Ipv6.sin6_family = IPv6;
+                    break;
+                default:
+                    throw new ArgumentException("Only IPv4 or IPv6 are supported");
+            }
+
+            SetPort(endpoint.Address.AddressFamily, ref socketAddress, endpoint.Port);
+            return socketAddress;
+        }
+
+        private static void SetPort(AddressFamily addressFamily, ref SOCKADDR_INET socketAddrInet, int originalPort)
+        {
+            var convertedPort = (ushort)IPAddress.HostToNetworkOrder((short)originalPort);
+            switch (addressFamily)
+            {
+                case AddressFamily.InterNetwork:
+                    socketAddrInet.Ipv4.sin_port = convertedPort;
+                    break;
+                case AddressFamily.InterNetworkV6:
+                default:
+                    socketAddrInet.Ipv6.sin6_port = convertedPort;
+                    break;
+            }
+        }
     }
 }

+ 443 - 0
src/Servers/Kestrel/Transport.MsQuic/src/Internal/MsQuicStream.cs

@@ -0,0 +1,443 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Buffers;
+using System.Diagnostics;
+using System.IO.Pipelines;
+using System.Runtime.InteropServices;
+using System.Security.Cryptography.X509Certificates;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Connections;
+using Microsoft.AspNetCore.Connections.Features;
+using Microsoft.AspNetCore.Http.Features;
+using Microsoft.Extensions.Logging;
+using static Microsoft.AspNetCore.Server.Kestrel.Transport.MsQuic.Internal.MsQuicNativeMethods;
+
+namespace Microsoft.AspNetCore.Server.Kestrel.Transport.MsQuic.Internal
+{
+    internal class MsQuicStream : TransportConnection, IUnidirectionalStreamFeature
+    {
+        private Task _processingTask;
+        private MsQuicConnection _connection;
+        private readonly CancellationTokenSource _streamClosedTokenSource = new CancellationTokenSource();
+        private IMsQuicTrace _log;
+        private bool _disposed;
+        private IntPtr _nativeObjPtr;
+        private GCHandle _handle;
+        private StreamCallbackDelegate _delegate;
+        private string _connectionId;
+
+        internal ResettableCompletionSource _resettableCompletion;
+        private MemoryHandle[] _bufferArrays;
+        private GCHandle _sendBuffer;
+
+        public MsQuicStream(MsQuicApi api, MsQuicConnection connection, MsQuicTransportContext context, QUIC_STREAM_OPEN_FLAG flags, IntPtr nativeObjPtr)
+        {
+            Debug.Assert(connection != null);
+
+            Api = api;
+            _nativeObjPtr = nativeObjPtr;
+
+            _connection = connection;
+            MemoryPool = context.Options.MemoryPoolFactory();
+            _log = context.Log;
+
+            ConnectionClosed = _streamClosedTokenSource.Token;
+
+            var maxReadBufferSize = context.Options.MaxReadBufferSize.Value;
+            var maxWriteBufferSize = context.Options.MaxWriteBufferSize.Value;
+            _resettableCompletion = new ResettableCompletionSource(this);
+
+            // TODO should we allow these PipeScheduler to be configurable here?
+            var inputOptions = new PipeOptions(MemoryPool, PipeScheduler.ThreadPool, PipeScheduler.Inline, maxReadBufferSize, maxReadBufferSize / 2, useSynchronizationContext: false);
+            var outputOptions = new PipeOptions(MemoryPool, PipeScheduler.Inline, PipeScheduler.ThreadPool, maxWriteBufferSize, maxWriteBufferSize / 2, useSynchronizationContext: false);
+
+            var pair = DuplexPipe.CreateConnectionPair(inputOptions, outputOptions);
+
+            // TODO when stream is unidirectional, don't create an output pipe.
+            if (flags.HasFlag(QUIC_STREAM_OPEN_FLAG.UNIDIRECTIONAL))
+            {
+                Features.Set<IUnidirectionalStreamFeature>(this);
+            }
+
+            // TODO populate the ITlsConnectionFeature (requires client certs). 
+            var feature = new FakeTlsConnectionFeature();
+            Features.Set<ITlsConnectionFeature>(feature);
+
+            Transport = pair.Transport;
+            Application = pair.Application;
+
+            SetCallbackHandler();
+
+            _processingTask = ProcessSends();
+
+            // Concatenate stream id with ConnectionId.
+            _log.NewStream(ConnectionId);
+        }
+
+        public override MemoryPool<byte> MemoryPool { get; }
+        public PipeWriter Input => Application.Output;
+        public PipeReader Output => Application.Input;
+
+        public override string ConnectionId {
+            get
+            {
+                if (_connectionId == null)
+                {
+                    _connectionId = $"{_connection.ConnectionId}:{base.ConnectionId}";
+                }
+                return _connectionId;
+            }
+            set
+            {
+                _connectionId = value;
+            }
+        }
+
+        private async Task ProcessSends()
+        {
+            var output = Output;
+            try
+            {
+                while (true)
+                {
+                    var result = await output.ReadAsync();
+                    _log.LogDebug(0, "Handling send event");
+
+                    if (result.IsCanceled)
+                    {
+                        // TODO how to get abort codepath sync'd
+                        ShutDown(QUIC_STREAM_SHUTDOWN_FLAG.ABORT, 0);
+                        break;
+                    }
+
+                    var buffer = result.Buffer;
+
+                    var end = buffer.End;
+                    var isCompleted = result.IsCompleted;
+                    if (!buffer.IsEmpty)
+                    {
+                        await SendAsync(buffer, QUIC_SEND_FLAG.NONE);
+                    }
+
+                    output.AdvanceTo(end);
+
+                    if (isCompleted)
+                    {
+                        // Once the stream pipe is closed, shutdown the stream.
+                        ShutDown(QUIC_STREAM_SHUTDOWN_FLAG.GRACEFUL, 0);
+                        break;
+                    }
+                }
+            }
+            catch (Exception)
+            {
+                ShutDown(QUIC_STREAM_SHUTDOWN_FLAG.ABORT, 0);
+            }
+        }
+
+        internal uint HandleEvent(ref MsQuicNativeMethods.StreamEvent evt)
+        {
+            var status = MsQuicConstants.Success;
+
+            switch (evt.Type)
+            {
+                case QUIC_STREAM_EVENT.START_COMPLETE:
+                    status = HandleStartComplete();
+                    break;
+                case QUIC_STREAM_EVENT.RECV:
+                    {
+                        HandleEventRecv(
+                            ref evt);
+                    }
+                    break;
+                case QUIC_STREAM_EVENT.SEND_COMPLETE:
+                    {
+                        status = HandleEventSendComplete(ref evt);
+                    }
+                    break;
+                case QUIC_STREAM_EVENT.PEER_SEND_CLOSE:
+                    {
+                        status = HandleEventPeerSendClose();
+                    }
+                    break;
+                // TODO figure out difference between SEND_ABORT and RECEIVE_ABORT
+                case QUIC_STREAM_EVENT.PEER_SEND_ABORT:
+                    {
+                        _streamClosedTokenSource.Cancel();
+                        status = HandleEventPeerSendAbort();
+                    }
+                    break;
+                case QUIC_STREAM_EVENT.PEER_RECV_ABORT:
+                    {
+                        _streamClosedTokenSource.Cancel();
+                        status = HandleEventPeerRecvAbort();
+                    }
+                    break;
+                case QUIC_STREAM_EVENT.SEND_SHUTDOWN_COMPLETE:
+                    {
+                        status = HandleEventSendShutdownComplete(ref evt);
+                    }
+                    break;
+                case QUIC_STREAM_EVENT.SHUTDOWN_COMPLETE:
+                    {
+                        Close();
+                        return MsQuicConstants.Success;
+                    }
+
+                default:
+                    break;
+            }
+            return status;
+        }
+
+        private uint HandleEventPeerRecvAbort()
+        {
+            return MsQuicConstants.Success;
+        }
+
+        private uint HandleEventPeerSendAbort()
+        {
+            return MsQuicConstants.Success;
+        }
+
+        private uint HandleStartComplete()
+        {
+            _resettableCompletion.Complete(MsQuicConstants.Success);
+            return MsQuicConstants.Success;
+        }
+
+        private uint HandleEventSendShutdownComplete(ref MsQuicNativeMethods.StreamEvent evt)
+        {
+            return MsQuicConstants.Success;
+        }
+
+        private uint HandleEventPeerSendClose()
+        {
+            Input.Complete();
+            return MsQuicConstants.Success;
+        }
+
+        public uint HandleEventSendComplete(ref MsQuicNativeMethods.StreamEvent evt)
+        {
+            _sendBuffer.Free();
+            foreach (var gchBufferArray in _bufferArrays)
+            {
+                gchBufferArray.Dispose();
+            }
+            _resettableCompletion.Complete(evt.Data.PeerRecvAbort.ErrorCode);
+            return MsQuicConstants.Success;
+        }
+
+        protected void HandleEventRecv(ref MsQuicNativeMethods.StreamEvent evt)
+        {
+            static unsafe void CopyToBuffer(Span<byte> buffer, StreamEvent evt)
+            {
+                var length = (int)evt.Data.Recv.Buffers[0].Length;
+                new Span<byte>(evt.Data.Recv.Buffers[0].Buffer, length).CopyTo(buffer);
+            }
+
+            _log.LogDebug(0, "Handling receive event");
+            var input = Input;
+            var length = (int)evt.Data.Recv.TotalBufferLength;
+            var result = input.GetSpan(length);
+            CopyToBuffer(result, evt);
+
+            input.Advance(length);
+
+            var flushTask = input.FlushAsync();
+
+            if (!flushTask.IsCompletedSuccessfully)
+            {
+                _ = AwaitFlush(flushTask);
+
+                return;
+            }
+
+            async Task AwaitFlush(ValueTask<FlushResult> ft)
+            {
+                await ft;
+                // TODO figure out when to call these for receive.
+                EnableReceive();
+                ReceiveComplete(length);
+            }
+        }
+
+        public override void Abort(ConnectionAbortedException abortReason)
+        {
+            Shutdown(abortReason);
+
+            // Cancel ProcessSends loop after calling shutdown to ensure the correct _shutdownReason gets set.
+            Output.CancelPendingRead();
+        }
+
+        private void Shutdown(Exception shutdownReason)
+        {
+        }
+
+        public MsQuicApi Api { get; set; }
+
+        internal static uint NativeCallbackHandler(
+           IntPtr stream,
+           IntPtr context,
+           StreamEvent connectionEventStruct)
+        {
+            var handle = GCHandle.FromIntPtr(context);
+            var quicStream = (MsQuicStream)handle.Target;
+
+            return quicStream.HandleEvent(ref connectionEventStruct);
+        }
+
+        public void SetCallbackHandler()
+        {
+            _handle = GCHandle.Alloc(this);
+
+            _delegate = new StreamCallbackDelegate(NativeCallbackHandler);
+            Api.SetCallbackHandlerDelegate(
+                _nativeObjPtr,
+                _delegate,
+                GCHandle.ToIntPtr(_handle));
+        }
+
+        public unsafe ValueTask<uint> SendAsync(
+           ReadOnlySequence<byte> buffers,
+           QUIC_SEND_FLAG flags)
+        {
+            var bufferCount = 0;
+            foreach (var memory in buffers)
+            {
+                bufferCount++;
+            }
+
+            var quicBufferArray = new QuicBuffer[bufferCount];
+            _bufferArrays = new MemoryHandle[bufferCount];
+
+            var i = 0;
+            foreach (var memory in buffers)
+            {
+                var handle = memory.Pin();
+                _bufferArrays[i] = handle;
+                quicBufferArray[i].Length = (uint)memory.Length;
+                quicBufferArray[i].Buffer = (byte*)handle.Pointer;
+                i++;
+            }
+
+            _sendBuffer = GCHandle.Alloc(quicBufferArray, GCHandleType.Pinned);
+
+            var quicBufferPointer = (QuicBuffer*)Marshal.UnsafeAddrOfPinnedArrayElement(quicBufferArray, 0);
+
+            var status = Api.StreamSendDelegate(
+                _nativeObjPtr,
+                quicBufferPointer,
+                (uint)bufferCount,
+                (uint)flags,
+                _nativeObjPtr);
+
+            MsQuicStatusException.ThrowIfFailed(status);
+
+            return _resettableCompletion.GetValueTask();
+        }
+
+        public ValueTask<uint> StartAsync()
+        {
+            var status = Api.StreamStartDelegate(
+              _nativeObjPtr,
+              (uint)QUIC_STREAM_START_FLAG.ASYNC);
+
+            MsQuicStatusException.ThrowIfFailed(status);
+            return _resettableCompletion.GetValueTask();
+        }
+
+        public void ReceiveComplete(int bufferLength)
+        {
+            var status = (uint)Api.StreamReceiveComplete(_nativeObjPtr, (ulong)bufferLength);
+            MsQuicStatusException.ThrowIfFailed(status);
+        }
+
+        public void ShutDown(
+            QUIC_STREAM_SHUTDOWN_FLAG flags,
+            ushort errorCode)
+        {
+            var status = (uint)Api.StreamShutdownDelegate(
+                _nativeObjPtr,
+                (uint)flags,
+                errorCode);
+            MsQuicStatusException.ThrowIfFailed(status);
+        }
+
+        public void Close()
+        {
+            var status = (uint)Api.StreamCloseDelegate?.Invoke(_nativeObjPtr);
+            MsQuicStatusException.ThrowIfFailed(status);
+        }
+
+        public void Dispose()
+        {
+            Dispose(true);
+            GC.SuppressFinalize(this);
+        }
+
+        public unsafe void EnableReceive()
+        {
+            var val = true;
+            var buffer = new QuicBuffer()
+            {
+                Length = sizeof(bool),
+                Buffer = (byte*)&val
+            };
+            SetParam(QUIC_PARAM_STREAM.RECEIVE_ENABLED, buffer);
+        }
+
+        private void SetParam(
+              QUIC_PARAM_STREAM param,
+              QuicBuffer buf)
+        {
+            MsQuicStatusException.ThrowIfFailed(Api.UnsafeSetParam(
+                _nativeObjPtr,
+                (uint)QUIC_PARAM_LEVEL.SESSION,
+                (uint)param,
+                buf));
+        }
+
+        ~MsQuicStream()
+        {
+            _log.LogDebug("Destructor");
+            Dispose(false);
+        }
+
+        private void Dispose(bool disposing)
+        {
+            if (_disposed)
+            {
+                return;
+            }
+
+            if (_nativeObjPtr != IntPtr.Zero)
+            {
+                Api.StreamCloseDelegate?.Invoke(_nativeObjPtr);
+            }
+
+            _handle.Free();
+            _nativeObjPtr = IntPtr.Zero;
+            Api = null;
+
+            _disposed = true;
+        }
+    }
+
+    internal class FakeTlsConnectionFeature : ITlsConnectionFeature
+    {
+        public FakeTlsConnectionFeature()
+        {
+        }
+
+        public X509Certificate2 ClientCertificate { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
+
+        public Task<X509Certificate2> GetClientCertificateAsync(CancellationToken cancellationToken)
+        {
+            throw new NotImplementedException();
+        }
+    }
+}

+ 53 - 0
src/Servers/Kestrel/Transport.MsQuic/src/Internal/MsQuicTrace.cs

@@ -0,0 +1,53 @@
+// 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.MsQuic.Internal
+{
+    internal class MsQuicTrace : IMsQuicTrace
+    {
+        private static readonly Action<ILogger, string, Exception> _acceptedConnection =
+            LoggerMessage.Define<string>(LogLevel.Debug, new EventId(4, nameof(NewConnection)), @"Connection id ""{ConnectionId}"" accepted.");
+        private static readonly Action<ILogger, string, Exception> _acceptedStream =
+            LoggerMessage.Define<string>(LogLevel.Debug, new EventId(5, nameof(NewStream)), @"Stream id ""{ConnectionId}"" accepted.");
+        private static readonly Action<ILogger, string, string, Exception> _connectionError =
+            LoggerMessage.Define<string, string>(LogLevel.Debug, new EventId(6, nameof(NewStream)), @"Connection id ""{ConnectionId}"" hit an exception: ""{Reason}"".");
+        private static readonly Action<ILogger, string, string, Exception> _streamError =
+            LoggerMessage.Define<string, string>(LogLevel.Debug, new EventId(7, nameof(NewStream)), @"Connection id ""{ConnectionId}"" hit an exception: ""{Reason}"".");
+
+        private ILogger _logger;
+
+        public MsQuicTrace(ILogger logger)
+        {
+            _logger = logger;
+        }
+
+        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);
+
+        public void NewConnection(string connectionId)
+        {
+            _acceptedConnection(_logger, connectionId, null);
+        }
+
+        public void NewStream(string streamId)
+        {
+            _acceptedStream(_logger, streamId, null);
+        }
+        public void ConnectionError(string connectionId, Exception ex)
+        {
+            _connectionError(_logger, connectionId, ex.Message, ex);
+        }
+
+        public void StreamError(string streamId, Exception ex)
+        {
+            _streamError(_logger, streamId, ex.Message, ex);
+        }
+    }
+}

+ 21 - 0
src/Servers/Kestrel/Transport.MsQuic/src/Internal/MsQuicTransportContext.cs

@@ -0,0 +1,21 @@
+// 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.Extensions.Hosting;
+
+namespace Microsoft.AspNetCore.Server.Kestrel.Transport.MsQuic.Internal
+{
+    internal class MsQuicTransportContext
+    {
+        public MsQuicTransportContext(IHostApplicationLifetime appLifetime, IMsQuicTrace log, MsQuicTransportOptions options)
+        {
+            AppLifetime = appLifetime;
+            Log = log;
+            Options = options;
+        }
+
+        public IHostApplicationLifetime AppLifetime { get; }
+        public IMsQuicTrace Log { get; }
+        public MsQuicTransportOptions Options { get; }
+    }
+}

+ 44 - 0
src/Servers/Kestrel/Transport.MsQuic/src/Internal/QuicSecConfig.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 System;
+
+namespace Microsoft.AspNetCore.Server.Kestrel.Transport.MsQuic.Internal
+{
+    internal class QuicSecConfig : IDisposable
+    {
+        private bool _disposed;
+        private MsQuicApi _registration;
+
+        public QuicSecConfig(MsQuicApi registration, IntPtr nativeObjPtr)
+        {
+            _registration = registration;
+            NativeObjPtr = nativeObjPtr;
+        }
+
+        public IntPtr NativeObjPtr { get; private set; }
+
+        public void Dispose()
+        {
+            Dispose(disposing: true);
+            GC.SuppressFinalize(this);
+        }
+
+        private void Dispose(bool disposing)
+        {
+            if (_disposed)
+            {
+                return;
+            }
+
+            _registration.SecConfigDeleteDelegate?.Invoke(NativeObjPtr);
+            NativeObjPtr = IntPtr.Zero;
+            _disposed = true;
+        }
+
+        ~QuicSecConfig()
+        {
+            Dispose(disposing: false);
+        }
+    }
+}

+ 141 - 0
src/Servers/Kestrel/Transport.MsQuic/src/Internal/QuicSession.cs

@@ -0,0 +1,141 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Net;
+using System.Threading.Tasks;
+using static Microsoft.AspNetCore.Server.Kestrel.Transport.MsQuic.Internal.MsQuicNativeMethods;
+
+namespace Microsoft.AspNetCore.Server.Kestrel.Transport.MsQuic.Internal
+{
+    internal sealed class QuicSession : IDisposable
+    {
+        private bool _disposed = false;
+        private IntPtr _nativeObjPtr;
+        private MsQuicApi _registration;
+
+        internal QuicSession(MsQuicApi registration, IntPtr nativeObjPtr)
+        {
+            _registration = registration;
+            _nativeObjPtr = nativeObjPtr;
+        }
+
+        public async ValueTask<MsQuicConnection> ConnectionOpenAsync(IPEndPoint endpoint, MsQuicTransportContext context)
+        {
+            var status = _registration.ConnectionOpenDelegate(
+                _nativeObjPtr,
+                MsQuicConnection.NativeCallbackHandler,
+                IntPtr.Zero,
+                out var connectionPtr);
+
+            MsQuicStatusException.ThrowIfFailed(status);
+
+            var msQuicConnection = new MsQuicConnection(_registration, context, connectionPtr);
+
+            await msQuicConnection.StartAsync((ushort)endpoint.AddressFamily, endpoint.Address.ToString(), (ushort)endpoint.Port);
+
+            return msQuicConnection;
+        }
+
+        internal IntPtr ListenerOpen(ListenerCallbackDelegate callback)
+        {
+            var status = _registration.ListenerOpenDelegate(
+                                        _nativeObjPtr,
+                                        callback,
+                                        IntPtr.Zero,
+                                        out var listenerPointer
+                                        );
+
+            MsQuicStatusException.ThrowIfFailed(status);
+
+            return listenerPointer;
+        }
+
+        public void ShutDown(
+            QUIC_CONNECTION_SHUTDOWN_FLAG Flags,
+            ushort ErrorCode)
+        {
+            _registration.SessionShutdownDelegate(
+                _nativeObjPtr,
+                (uint)Flags,
+                ErrorCode);
+        }
+
+        public void Dispose()
+        {
+            Dispose(true);
+            GC.SuppressFinalize(this);
+        }
+
+        public void SetPeerBiDirectionalStreamCount(ushort count)
+        {
+            SetUshortParamter(QUIC_PARAM_SESSION.PEER_BIDI_STREAM_COUNT, count);
+        }
+
+        public void SetPeerUnidirectionalStreamCount(ushort count)
+        {
+            SetUshortParamter(QUIC_PARAM_SESSION.PEER_UNIDI_STREAM_COUNT, count);
+        }
+
+        private unsafe void SetUshortParamter(QUIC_PARAM_SESSION param, ushort count)
+        {
+            var buffer = new MsQuicNativeMethods.QuicBuffer()
+            {
+                Length = sizeof(ushort),
+                Buffer = (byte*)&count
+            };
+
+            SetParam(param, buffer);
+        }
+
+        public void SetDisconnectTimeout(TimeSpan timeout)
+        {
+            SetULongParamter(QUIC_PARAM_SESSION.DISCONNECT_TIMEOUT, (ulong)timeout.TotalMilliseconds);
+        }
+
+        public void SetIdleTimeout(TimeSpan timeout)
+        {
+            SetULongParamter(QUIC_PARAM_SESSION.IDLE_TIMEOUT, (ulong)timeout.TotalMilliseconds);
+
+        }
+        private unsafe void SetULongParamter(QUIC_PARAM_SESSION param, ulong count)
+        {
+            var buffer = new MsQuicNativeMethods.QuicBuffer()
+            {
+                Length = sizeof(ulong),
+                Buffer = (byte*)&count
+            };
+            SetParam(param, buffer);
+        }
+
+        private void SetParam(
+          QUIC_PARAM_SESSION param,
+          MsQuicNativeMethods.QuicBuffer buf)
+        {
+            MsQuicStatusException.ThrowIfFailed(_registration.UnsafeSetParam(
+                _nativeObjPtr,
+                (uint)QUIC_PARAM_LEVEL.SESSION,
+                (uint)param,
+                buf));
+        }
+
+        ~QuicSession()
+        {
+            Dispose(false);
+        }
+
+        private void Dispose(bool disposing)
+        {
+            if (_disposed)
+            {
+                return;
+            }
+
+            _registration.SessionCloseDelegate?.Invoke(_nativeObjPtr);
+            _nativeObjPtr = IntPtr.Zero;
+            _registration = null;
+
+            _disposed = true;
+        }
+    }
+}

+ 58 - 0
src/Servers/Kestrel/Transport.MsQuic/src/Internal/ResettableCompletionSource.cs

@@ -0,0 +1,58 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Threading.Tasks;
+using System.Threading.Tasks.Sources;
+
+namespace Microsoft.AspNetCore.Server.Kestrel.Transport.MsQuic.Internal
+{
+    internal class ResettableCompletionSource : IValueTaskSource<uint>
+    {
+        private ManualResetValueTaskSourceCore<uint> _valueTaskSource;
+        private readonly MsQuicStream _stream;
+
+        public ResettableCompletionSource(MsQuicStream stream)
+        {
+            _stream = stream;
+            _valueTaskSource.RunContinuationsAsynchronously = true;
+        }
+
+        public ValueTask<uint> GetValueTask()
+        {
+            return new ValueTask<uint>(this, _valueTaskSource.Version);
+        }
+
+        public uint GetResult(short token)
+        {
+            var isValid = token == _valueTaskSource.Version;
+            try
+            {
+                return _valueTaskSource.GetResult(token);
+            }
+            finally
+            {
+                if (isValid)
+                {
+                    _valueTaskSource.Reset();
+                    _stream._resettableCompletion = this;
+                }
+            }
+        }
+
+        public ValueTaskSourceStatus GetStatus(short token)
+        {
+            return _valueTaskSource.GetStatus(token);
+        }
+
+        public void OnCompleted(Action<object> continuation, object state, short token, ValueTaskSourceOnCompletedFlags flags)
+        {
+            _valueTaskSource.OnCompleted(continuation, state, token, flags);
+        }
+
+        public void Complete(uint result)
+        {
+            _valueTaskSource.SetResult(result);
+        }
+    }
+}

+ 25 - 0
src/Servers/Kestrel/Transport.MsQuic/src/Internal/UIntExtensions.cs

@@ -0,0 +1,25 @@
+// 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.Runtime.InteropServices;
+
+namespace Microsoft.AspNetCore.Server.Kestrel.Transport.MsQuic.Internal
+{
+    internal static class UIntExtensions
+    {
+        internal static bool Succeeded(this uint status)
+        {
+            if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+            {
+                return status >= 0x80000000;
+            }
+
+            if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
+            {
+                return (int)status <= 0;
+            }
+
+            return false;
+        }
+    }
+}

+ 24 - 1
src/Servers/Kestrel/Transport.MsQuic/src/Microsoft.AspNetCore.Server.Kestrel.Transport.MsQuic.csproj

@@ -10,11 +10,34 @@
     <IsShippingPackage>false</IsShippingPackage>
   </PropertyGroup>
 
+  <ItemGroup>
+    <Compile Include="$(RepoRoot)src\Shared\Buffers.MemoryPool\*.cs" LinkBase="MemoryPool" />
+    <Compile Include="$(KestrelSharedSourceRoot)\CorrelationIdGenerator.cs" Link="Internal\CorrelationIdGenerator.cs" />
+    <Compile Include="$(KestrelSharedSourceRoot)\DuplexPipe.cs" Link="Internal\DuplexPipe.cs" />
+    <Compile Include="$(KestrelSharedSourceRoot)\TransportConnection.cs" Link="Internal\TransportConnection.cs" />
+    <Compile Include="$(KestrelSharedSourceRoot)\TransportConnection.Generated.cs" Link="Internal\TransportConnection.Generated.cs" />
+    <Compile Include="$(KestrelSharedSourceRoot)\TransportConnection.FeatureCollection.cs" Link="Internal\TransportConnection.FeatureCollection.cs" />
+  </ItemGroup>
+  
   <ItemGroup>
     <Reference Include="Microsoft.AspNetCore.Hosting.Abstractions" />
     <Reference Include="Microsoft.AspNetCore.Connections.Abstractions" />
     <Reference Include="Microsoft.Extensions.Logging.Abstractions" />
     <Reference Include="Microsoft.Extensions.Options" />
   </ItemGroup>
-
+  
+  <ItemGroup>
+    <Content Include="msquic.dll" Condition="Exists('msquic.dll')">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+      <CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
+    </Content>
+    <Content Include="libmsquic.so" Condition="Exists('libmsquic.so')">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+      <CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
+    </Content>
+    <Content Include="msquic.pdb" Condition="Exists('msquic.pdb')">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+      <CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
+    </Content>
+  </ItemGroup>
 </Project>

+ 64 - 0
src/Servers/Kestrel/Transport.MsQuic/src/MsQuicConnectionFactory.cs

@@ -0,0 +1,64 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Net;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Connections;
+using Microsoft.AspNetCore.Server.Kestrel.Transport.MsQuic.Internal;
+using Microsoft.Extensions.Hosting;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Options;
+
+namespace Microsoft.AspNetCore.Server.Kestrel.Transport.MsQuic
+{
+    public class MsQuicConnectionFactory : IConnectionFactory
+    {
+        private MsQuicApi _api;
+        private QuicSession _session;
+        private bool _started;
+        private MsQuicTransportContext _transportContext;
+
+        public MsQuicConnectionFactory(IOptions<MsQuicTransportOptions> options, IHostApplicationLifetime lifetime, ILoggerFactory loggerFactory)
+        {
+            if (options == null)
+            {
+                throw new ArgumentNullException(nameof(options));
+            }
+            _api = new MsQuicApi();
+            var logger = loggerFactory.CreateLogger("Microsoft.AspNetCore.Server.Kestrel.Transport.MsQuic.Client");
+            var trace = new MsQuicTrace(logger);
+
+            _transportContext = new MsQuicTransportContext(lifetime, trace, options.Value);
+        }
+
+        public async ValueTask<ConnectionContext> ConnectAsync(EndPoint endPoint, CancellationToken cancellationToken = default)
+        {
+            if (!(endPoint is IPEndPoint ipEndPoint))
+            {
+                throw new NotSupportedException($"{endPoint} is not supported");
+            }
+
+            if (!_started)
+            {
+                _started = true;
+                await StartAsync();
+            }
+
+            var connection = await _session.ConnectionOpenAsync(endPoint as IPEndPoint, _transportContext);
+            return connection;
+        }
+
+        private ValueTask StartAsync()
+        {
+            _api.RegistrationOpen(Encoding.ASCII.GetBytes(_transportContext.Options.RegistrationName));
+            _session = _api.SessionOpen(_transportContext.Options.Alpn);
+            _session.SetIdleTimeout(_transportContext.Options.IdleTimeout);
+            _session.SetPeerBiDirectionalStreamCount(_transportContext.Options.MaxBidirectionalStreamCount);
+            _session.SetPeerUnidirectionalStreamCount(_transportContext.Options.MaxBidirectionalStreamCount);
+            return new ValueTask();
+        }
+    }
+}

+ 47 - 0
src/Servers/Kestrel/Transport.MsQuic/src/MsQuicTransportFactory.cs

@@ -0,0 +1,47 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Net;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Connections;
+using Microsoft.AspNetCore.Server.Kestrel.Transport.MsQuic.Internal;
+using Microsoft.Extensions.Hosting;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Options;
+
+namespace Microsoft.AspNetCore.Server.Kestrel.Transport.MsQuic
+{
+    public class MsQuicTransportFactory : IConnectionListenerFactory
+    {
+        private MsQuicTrace _log;
+        private IHostApplicationLifetime _applicationLifetime;
+        private MsQuicTransportOptions _options;
+
+        public MsQuicTransportFactory(IHostApplicationLifetime applicationLifetime, ILoggerFactory loggerFactory, IOptions<MsQuicTransportOptions> options)
+        {
+            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.MsQuic");
+            _log = new MsQuicTrace(logger);
+            _applicationLifetime = applicationLifetime;
+            _options = options.Value;
+        }
+
+        public async ValueTask<IConnectionListener> BindAsync(EndPoint endpoint, CancellationToken cancellationToken = default)
+        {
+            var transport = new MsQuicConnectionListener(_options, _applicationLifetime, _log, endpoint);
+            await transport.BindAsync();
+            return transport;
+        }
+    }
+}

+ 55 - 0
src/Servers/Kestrel/Transport.MsQuic/src/MsQuicTransportOptions.cs

@@ -0,0 +1,55 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Buffers;
+using System.Security.Cryptography.X509Certificates;
+
+namespace Microsoft.AspNetCore.Server.Kestrel.Transport.MsQuic
+{
+    public class MsQuicTransportOptions
+    {
+        /// <summary>
+        /// The maximum number of concurrent bi-directional streams per connection.
+        /// </summary>
+        public ushort MaxBidirectionalStreamCount { get; set; } = 100;
+
+        /// <summary>
+        /// The maximum number of concurrent inbound uni-directional streams per connection.
+        /// </summary>
+        public ushort MaxUnidirectionalStreamCount { get; set; } = 10;
+
+        /// <summary>
+        /// The Application Layer Protocol Negotiation string.
+        /// </summary>
+        public string Alpn { get; set; }
+
+        /// <summary>
+        /// The registration name to use in MsQuic.
+        /// </summary>
+        public string RegistrationName { get; set; }
+
+        /// <summary>
+        /// The certificate that MsQuic will use.
+        /// </summary>
+        public X509Certificate2 Certificate { get; set; }
+
+        /// <summary>
+        /// Sets the idle timeout for connections and streams.
+        /// </summary>
+        public TimeSpan IdleTimeout { get; set; }
+
+        /// <summary>
+        /// The maximum read size.
+        /// </summary>
+        public long? MaxReadBufferSize { get; set; } = 1024 * 1024;
+
+        /// <summary>
+        /// The maximum write size.
+        /// </summary>
+        public long? MaxWriteBufferSize { get; set; } = 64 * 1024;
+
+        internal Func<MemoryPool<byte>> MemoryPoolFactory { get; set; } = System.Buffers.SlabMemoryPoolFactory.Create;
+
+    }
+}

+ 30 - 0
src/Servers/Kestrel/Transport.MsQuic/src/WebHostBuilderMsQuicExtensions.cs

@@ -0,0 +1,30 @@
+// 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.AspNetCore.Connections;
+using Microsoft.AspNetCore.Server.Kestrel.Transport.MsQuic;
+using Microsoft.AspNetCore.Server.Kestrel.Transport.MsQuic.Internal;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace Microsoft.AspNetCore.Hosting
+{
+    public static class WebHostBuilderMsQuicExtensions
+    {
+        public static IWebHostBuilder UseMsQuic(this IWebHostBuilder hostBuilder)
+        {
+            return hostBuilder.ConfigureServices(services =>
+            {
+                services.AddSingleton<IConnectionListenerFactory, MsQuicTransportFactory>();
+            });
+        }
+
+        public static IWebHostBuilder UseMsQuic(this IWebHostBuilder hostBuilder, Action<MsQuicTransportOptions> configureOptions)
+        {
+            return hostBuilder.UseMsQuic().ConfigureServices(services =>
+            {
+                services.Configure(configureOptions);
+            });
+        }
+    }
+}

+ 104 - 0
src/Servers/Kestrel/samples/QuicSampleApp/Program.cs

@@ -0,0 +1,104 @@
+using System;
+using System.Buffers;
+using System.Net;
+using System.Security.Cryptography.X509Certificates;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Connections;
+using Microsoft.AspNetCore.Connections.Features;
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.AspNetCore.Server.Kestrel.Https;
+using Microsoft.Extensions.Logging;
+using System.Diagnostics;
+
+namespace QuicSampleApp
+{
+    public class Startup
+    {
+        public void Configure(IApplicationBuilder app)
+        {
+            app.Run((httpContext) =>
+            {
+                return Task.CompletedTask;
+            });
+        }
+
+        public static void Main(string[] args)
+        {
+            var cert = CertificateLoader.LoadFromStoreCert("localhost", StoreName.My.ToString(), StoreLocation.CurrentUser, true);
+            var hostBuilder = new WebHostBuilder()
+                 .ConfigureLogging((_, factory) =>
+                 {
+                     factory.SetMinimumLevel(LogLevel.Debug);
+                     factory.AddConsole();
+                 })
+                 .UseKestrel()
+                 .UseMsQuic(options =>
+                 {
+                     options.Certificate = cert;
+                     options.RegistrationName = "AspNetCore-MsQuic";
+                     options.Alpn = "QuicTest";
+                     options.IdleTimeout = TimeSpan.FromHours(1);
+                 })
+                 .ConfigureKestrel((context, options) =>
+                 {
+                     var basePort = 5555;
+
+                     options.Listen(IPAddress.Any, basePort, listenOptions =>
+                     {
+                         listenOptions.Use((next) =>
+                         {
+                             return async connection =>
+                             {
+                                 var streamFeature = connection.Features.Get<IQuicStreamListenerFeature>();
+                                 if (streamFeature != null)
+                                 {
+                                     while (true)
+                                     {
+                                         var connectionContext = await streamFeature.AcceptAsync();
+                                         if (connectionContext == null)
+                                         {
+                                             return;
+                                         }
+                                         _ = next(connectionContext);
+                                     }
+                                 }
+                                 else
+                                 {
+                                     await next(connection);
+                                 }
+                             };
+                         });
+
+                         async Task EchoServer(ConnectionContext connection)
+                         {
+                             // For graceful shutdown
+                             try
+                             {
+                                 while (true)
+                                 {
+                                     var result = await connection.Transport.Input.ReadAsync();
+
+                                     if (result.IsCompleted)
+                                     {
+                                         break;
+                                     }
+
+                                     await connection.Transport.Output.WriteAsync(result.Buffer.ToArray());
+
+                                     connection.Transport.Input.AdvanceTo(result.Buffer.End);
+                                 }
+                             }
+                             catch (OperationCanceledException)
+                             {
+                             }
+                         }
+                         listenOptions.Run(EchoServer);
+                     });
+                 })
+                 .UseStartup<Startup>();
+
+            hostBuilder.Build().Run();
+        }
+    }
+}

+ 16 - 0
src/Servers/Kestrel/samples/QuicSampleApp/QuicSampleApp.csproj

@@ -0,0 +1,16 @@
+<Project Sdk="Microsoft.NET.Sdk.Web">
+  
+  <PropertyGroup>
+    <TargetFramework>$(DefaultNetCoreTargetFramework)</TargetFramework>
+    <IsPackable>false</IsPackable>
+    <NoDefaultLaunchSettingsFile>true</NoDefaultLaunchSettingsFile>
+    <GenerateRazorAssemblyInfo>false</GenerateRazorAssemblyInfo>
+  </PropertyGroup>
+  
+  <ItemGroup>
+    <Reference Include="Microsoft.AspNetCore.Server.Kestrel" />
+    <Reference Include="Microsoft.Extensions.Logging.Console" />
+    <Reference Include="Microsoft.AspNetCore.Server.Kestrel.Transport.MsQuic" />
+  </ItemGroup>
+
+</Project>

+ 106 - 0
src/Servers/Kestrel/samples/QuicSampleClient/Program.cs

@@ -0,0 +1,106 @@
+using System;
+using System.Buffers;
+using System.Net;
+using System.Security.Cryptography.X509Certificates;
+using System.Text;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Server.Kestrel.Https;
+using Microsoft.AspNetCore.Server.Kestrel.Transport.MsQuic;
+using Microsoft.AspNetCore.Connections.Abstractions.Features;
+using Microsoft.Extensions.Hosting;
+using Microsoft.AspNetCore.Connections;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace QuicSampleClient
+{
+    class Program
+    {
+        static async Task Main(string[] args)
+        {
+            var host = new HostBuilder()
+               .ConfigureLogging(loggingBuilder =>
+               {
+                   loggingBuilder.AddConsole();
+                   loggingBuilder.SetMinimumLevel(LogLevel.Error);
+               })
+               .ConfigureServices(services =>
+               {
+                   services.AddSingleton<IConnectionFactory, MsQuicConnectionFactory>();
+                   services.AddSingleton<MsQuicClientService>();
+                   services.AddOptions<MsQuicTransportOptions>();
+                   services.Configure<MsQuicTransportOptions>((options) =>
+                   {
+                       options.Alpn = "QuicTest";
+                       options.RegistrationName = "Quic-AspNetCore-client";
+                       options.Certificate = CertificateLoader.LoadFromStoreCert("localhost", StoreName.My.ToString(), StoreLocation.CurrentUser, true);
+                       options.IdleTimeout = TimeSpan.FromHours(1);
+                   });
+               })
+               .Build();
+            await host.Services.GetService<MsQuicClientService>().RunAsync();
+        }
+
+        private class MsQuicClientService
+        {
+            private readonly IConnectionFactory _connectionFactory;
+            private readonly ILogger<MsQuicClientService> _logger;
+            public MsQuicClientService(IConnectionFactory connectionFactory, ILogger<MsQuicClientService> logger)
+            {
+                _connectionFactory = connectionFactory;
+                _logger = logger;
+            }
+
+            public async Task RunAsync()
+            {
+                var connectionContext = await _connectionFactory.ConnectAsync(new IPEndPoint(IPAddress.Loopback, 5555));
+                var createStreamFeature = connectionContext.Features.Get<IQuicCreateStreamFeature>();
+                var streamContext = await createStreamFeature.StartBidirectionalStreamAsync();
+
+                Console.CancelKeyPress += new ConsoleCancelEventHandler((sender, args) =>
+                {
+                    streamContext.Transport.Input.CancelPendingRead();
+                    streamContext.Transport.Output.CancelPendingFlush();
+                });
+
+                var input = "asdf";
+                while (true)
+                {
+                    try
+                    {
+                        //var input = Console.ReadLine();
+                        if (input.Length == 0)
+                        {
+                            continue;
+                        }
+                        var flushResult = await streamContext.Transport.Output.WriteAsync(Encoding.ASCII.GetBytes(input));
+                        if (flushResult.IsCanceled)
+                        {
+                            break;
+                        }
+
+                        var readResult = await streamContext.Transport.Input.ReadAsync();
+                        if (readResult.IsCanceled)
+                        {
+                            break;
+                        }
+                        if (readResult.Buffer.Length > 0)
+                        {
+                            Console.WriteLine(Encoding.ASCII.GetString(readResult.Buffer.ToArray()));
+                        }
+
+                        streamContext.Transport.Input.AdvanceTo(readResult.Buffer.End);
+                    }
+                    catch (Exception ex)
+                    {
+                        Console.WriteLine(ex.Message);
+                        break;
+                    }
+                }
+
+                await streamContext.Transport.Input.CompleteAsync();
+                await streamContext.Transport.Output.CompleteAsync();
+            }
+        }
+    }
+}

+ 15 - 0
src/Servers/Kestrel/samples/QuicSampleClient/QuicSampleClient.csproj

@@ -0,0 +1,15 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <OutputType>Exe</OutputType>
+    <TargetFramework>$(DefaultNetCoreTargetFramework)</TargetFramework>
+    <IsPackable>false</IsPackable>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <Reference Include="Microsoft.AspNetCore.Server.Kestrel.Transport.MsQuic" />
+    <Reference Include="Microsoft.AspNetCore.Server.Kestrel" />
+    <Reference Include="Microsoft.Extensions.Hosting" />
+    <Reference Include="Microsoft.Extensions.Logging.Console" />
+  </ItemGroup>
+</Project>

+ 1 - 1
src/Servers/Kestrel/shared/DuplexPipe.cs

@@ -1,4 +1,4 @@
-// Copyright (c) Microsoft. All rights reserved.
+// Copyright (c) Microsoft. All rights reserved.
 // Licensed under the MIT license. See LICENSE file in the project root for full license information.
 
 namespace System.IO.Pipelines

+ 1 - 2
src/Tools/dotnet-watch/test/DotNetWatcherTests.cs

@@ -22,8 +22,7 @@ namespace Microsoft.DotNet.Watcher.Tools.FunctionalTests
             _app = new KitchenSinkApp(logger);
         }
 
-        [ConditionalFact]
-        [SkipOnHelix("https://github.com/aspnet/AspNetCore/issues/8267")]
+        [Fact(Skip = "https://github.com/aspnet/AspNetCore/issues/16912")]
         public async Task RunsWithDotnetWatchEnvVariable()
         {
             Assert.True(string.IsNullOrEmpty(Environment.GetEnvironmentVariable("DOTNET_WATCH")), "DOTNET_WATCH cannot be set already when this test is running");

+ 1 - 2
src/Tools/dotnet-watch/test/GlobbingAppTests.cs

@@ -85,8 +85,7 @@ namespace Microsoft.DotNet.Watcher.Tools.FunctionalTests
             await _app.HasRestarted();
         }
 
-        [ConditionalFact]
-        [SkipOnHelix("https://github.com/aspnet/AspNetCore/issues/8267")]
+        [Fact(Skip = "https://github.com/aspnet/AspNetCore/issues/16912")]
         public async Task ChangeExcludedFile()
         {
             await _app.StartWatcherAsync();

+ 1 - 2
src/Tools/dotnet-watch/test/NoDepsAppTests.cs

@@ -42,8 +42,7 @@ namespace Microsoft.DotNet.Watcher.Tools.FunctionalTests
             Assert.NotEqual(processIdentifier, processIdentifier2);
         }
 
-        [ConditionalFact]
-        [SkipOnHelix("https://github.com/aspnet/AspNetCore/issues/8267")]
+        [Fact(Skip = "https://github.com/aspnet/AspNetCore/issues/16912")]
         public async Task RestartProcessThatTerminatesAfterFileChange()
         {
             await _app.StartWatcherAsync();