Initial port of MsQuic transport (#15375)

This commit is contained in:
Justin Kotalik 2019-10-31 17:38:47 -07:00 committed by GitHub
parent fece4705ee
commit 0e8fea6fee
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 1181 additions and 0 deletions

View File

@ -60,6 +60,7 @@
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Server.Kestrel.Core" ProjectPath="$(RepoRoot)src\Servers\Kestrel\Core\src\Microsoft.AspNetCore.Server.Kestrel.Core.csproj" RefProjectPath="$(RepoRoot)src\Servers\Kestrel\Core\ref\Microsoft.AspNetCore.Server.Kestrel.Core.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Server.Kestrel" ProjectPath="$(RepoRoot)src\Servers\Kestrel\Kestrel\src\Microsoft.AspNetCore.Server.Kestrel.csproj" RefProjectPath="$(RepoRoot)src\Servers\Kestrel\Kestrel\ref\Microsoft.AspNetCore.Server.Kestrel.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv" ProjectPath="$(RepoRoot)src\Servers\Kestrel\Transport.Libuv\src\Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.csproj" RefProjectPath="$(RepoRoot)src\Servers\Kestrel\Transport.Libuv\ref\Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Server.Kestrel.Transport.MsQuic" ProjectPath="$(RepoRoot)src\Servers\Kestrel\Transport.MsQuic\src\Microsoft.AspNetCore.Server.Kestrel.Transport.MsQuic.csproj" RefProjectPath="$(RepoRoot)src\Servers\Kestrel\Transport.MsQuic\ref\Microsoft.AspNetCore.Server.Kestrel.Transport.MsQuic.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets" ProjectPath="$(RepoRoot)src\Servers\Kestrel\Transport.Sockets\src\Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets.csproj" RefProjectPath="$(RepoRoot)src\Servers\Kestrel\Transport.Sockets\ref\Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Authentication.Certificate" ProjectPath="$(RepoRoot)src\Security\Authentication\Certificate\src\Microsoft.AspNetCore.Authentication.Certificate.csproj" RefProjectPath="$(RepoRoot)src\Security\Authentication\Certificate\ref\Microsoft.AspNetCore.Authentication.Certificate.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Authentication.Cookies" ProjectPath="$(RepoRoot)src\Security\Authentication\Cookies\src\Microsoft.AspNetCore.Authentication.Cookies.csproj" RefProjectPath="$(RepoRoot)src\Security\Authentication\Cookies\ref\Microsoft.AspNetCore.Authentication.Cookies.csproj" />

View File

@ -84,6 +84,8 @@ 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}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -466,6 +468,18 @@ 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
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -503,6 +517,7 @@ 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}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {48207B50-7D05-4B10-B585-890FE0F4FCE1}

View File

@ -0,0 +1,10 @@
## Using MsQuic on Windows
### Setup pre-requisites
1. Update machine to the latest Windows Insiders build (build number 19010 or later). This is required for TLS 1.3 support.
2. Copy msquic.dll and msquic.pdb to this directory and uncomment the copy task in Microsoft.AspNetCore.Server.Kestrel.Transport.MsQuic.csproj. This will copy the msquic.dll into any built project.
For external contributors, msquic.dll isn't available publicly yet. See https://github.com/aspnet/Announcements/issues/393.
Credit to Diwakar Mantha and the Kaizala team for the MsQuic interop code.

View File

@ -0,0 +1,13 @@
<!-- This file is automatically generated. -->
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>$(DefaultNetCoreTargetFramework)</TargetFrameworks>
</PropertyGroup>
<ItemGroup Condition="'$(TargetFramework)' == '$(DefaultNetCoreTargetFramework)'">
<Compile Include="Microsoft.AspNetCore.Server.Kestrel.Transport.MsQuic.netcoreapp.cs" />
<Reference Include="Microsoft.AspNetCore.Hosting.Abstractions" />
<Reference Include="Microsoft.AspNetCore.Connections.Abstractions" />
<Reference Include="Microsoft.Extensions.Logging.Abstractions" />
<Reference Include="Microsoft.Extensions.Options" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,3 @@
// 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.

View File

@ -0,0 +1,186 @@
// 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;
namespace Microsoft.AspNetCore.Server.Kestrel.Transport.MsQuic.Internal
{
internal class MsQuicApi : IDisposable
{
private bool _disposed = false;
private IntPtr _registrationContext;
internal unsafe MsQuicApi()
{
var status = (uint)MsQuicNativeMethods.MsQuicOpen(version: 1, out var registration);
MsQuicStatusException.ThrowIfFailed(status);
NativeRegistration = *registration;
RegistrationOpenDelegate =
Marshal.GetDelegateForFunctionPointer<MsQuicNativeMethods.RegistrationOpenDelegate>(
NativeRegistration.RegistrationOpen);
RegistrationCloseDelegate =
Marshal.GetDelegateForFunctionPointer<MsQuicNativeMethods.RegistrationCloseDelegate>(
NativeRegistration.RegistrationClose);
SecConfigCreateDelegate =
Marshal.GetDelegateForFunctionPointer<MsQuicNativeMethods.SecConfigCreateDelegate>(
NativeRegistration.SecConfigCreate);
SecConfigDeleteDelegate =
Marshal.GetDelegateForFunctionPointer<MsQuicNativeMethods.SecConfigDeleteDelegate>(
NativeRegistration.SecConfigDelete);
SessionOpenDelegate =
Marshal.GetDelegateForFunctionPointer<MsQuicNativeMethods.SessionOpenDelegate>(
NativeRegistration.SessionOpen);
SessionCloseDelegate =
Marshal.GetDelegateForFunctionPointer<MsQuicNativeMethods.SessionCloseDelegate>(
NativeRegistration.SessionClose);
SessionShutdownDelegate =
Marshal.GetDelegateForFunctionPointer<MsQuicNativeMethods.SessionShutdownDelegate>(
NativeRegistration.SessionShutdown);
ListenerOpenDelegate =
Marshal.GetDelegateForFunctionPointer<MsQuicNativeMethods.ListenerOpenDelegate>(
NativeRegistration.ListenerOpen);
ListenerCloseDelegate =
Marshal.GetDelegateForFunctionPointer<MsQuicNativeMethods.ListenerCloseDelegate>(
NativeRegistration.ListenerClose);
ListenerStartDelegate =
Marshal.GetDelegateForFunctionPointer<MsQuicNativeMethods.ListenerStartDelegate>(
NativeRegistration.ListenerStart);
ListenerStopDelegate =
Marshal.GetDelegateForFunctionPointer<MsQuicNativeMethods.ListenerStopDelegate>(
NativeRegistration.ListenerStop);
ConnectionOpenDelegate =
Marshal.GetDelegateForFunctionPointer<MsQuicNativeMethods.ConnectionOpenDelegate>(
NativeRegistration.ConnectionOpen);
ConnectionCloseDelegate =
Marshal.GetDelegateForFunctionPointer<MsQuicNativeMethods.ConnectionCloseDelegate>(
NativeRegistration.ConnectionClose);
ConnectionShutdownDelegate =
Marshal.GetDelegateForFunctionPointer<MsQuicNativeMethods.ConnectionShutdownDelegate>(
NativeRegistration.ConnectionShutdown);
ConnectionStartDelegate =
Marshal.GetDelegateForFunctionPointer<MsQuicNativeMethods.ConnectionStartDelegate>(
NativeRegistration.ConnectionStart);
StreamOpenDelegate =
Marshal.GetDelegateForFunctionPointer<MsQuicNativeMethods.StreamOpenDelegate>(
NativeRegistration.StreamOpen);
StreamCloseDelegate =
Marshal.GetDelegateForFunctionPointer<MsQuicNativeMethods.StreamCloseDelegate>(
NativeRegistration.StreamClose);
StreamStartDelegate =
Marshal.GetDelegateForFunctionPointer<MsQuicNativeMethods.StreamStartDelegate>(
NativeRegistration.StreamStart);
StreamShutdownDelegate =
Marshal.GetDelegateForFunctionPointer<MsQuicNativeMethods.StreamShutdownDelegate>(
NativeRegistration.StreamShutdown);
StreamSendDelegate =
Marshal.GetDelegateForFunctionPointer<MsQuicNativeMethods.StreamSendDelegate>(
NativeRegistration.StreamSend);
SetContextDelegate =
Marshal.GetDelegateForFunctionPointer<MsQuicNativeMethods.SetContextDelegate>(
NativeRegistration.SetContext);
GetContextDelegate =
Marshal.GetDelegateForFunctionPointer<MsQuicNativeMethods.GetContextDelegate>(
NativeRegistration.GetContext);
SetCallbackHandlerDelegate =
Marshal.GetDelegateForFunctionPointer<MsQuicNativeMethods.SetCallbackHandlerDelegate>(
NativeRegistration.SetCallbackHandler);
SetParamDelegate =
Marshal.GetDelegateForFunctionPointer<MsQuicNativeMethods.SetParamDelegate>(
NativeRegistration.SetParam);
GetParamDelegate =
Marshal.GetDelegateForFunctionPointer<MsQuicNativeMethods.GetParamDelegate>(
NativeRegistration.GetParam);
}
internal MsQuicNativeMethods.NativeApi NativeRegistration { get; private set; }
internal MsQuicNativeMethods.RegistrationOpenDelegate RegistrationOpenDelegate { get; private set; }
internal MsQuicNativeMethods.RegistrationCloseDelegate RegistrationCloseDelegate { get; private set; }
internal MsQuicNativeMethods.SecConfigCreateDelegate SecConfigCreateDelegate { get; private set; }
internal MsQuicNativeMethods.SecConfigCreateCompleteDelegate SecConfigCreateCompleteDelegate { get; private set; }
internal MsQuicNativeMethods.SecConfigDeleteDelegate SecConfigDeleteDelegate { get; private set; }
internal MsQuicNativeMethods.SessionOpenDelegate SessionOpenDelegate { get; private set; }
internal MsQuicNativeMethods.SessionCloseDelegate SessionCloseDelegate { get; private set; }
internal MsQuicNativeMethods.SessionShutdownDelegate SessionShutdownDelegate { get; private set; }
internal MsQuicNativeMethods.ListenerOpenDelegate ListenerOpenDelegate { get; private set; }
internal MsQuicNativeMethods.ListenerCloseDelegate ListenerCloseDelegate { get; private set; }
internal MsQuicNativeMethods.ListenerStartDelegate ListenerStartDelegate { get; private set; }
internal MsQuicNativeMethods.ListenerStopDelegate ListenerStopDelegate { get; private set; }
internal MsQuicNativeMethods.ConnectionOpenDelegate ConnectionOpenDelegate { get; private set; }
internal MsQuicNativeMethods.ConnectionCloseDelegate ConnectionCloseDelegate { get; private set; }
internal MsQuicNativeMethods.ConnectionShutdownDelegate ConnectionShutdownDelegate { get; private set; }
internal MsQuicNativeMethods.ConnectionStartDelegate ConnectionStartDelegate { get; private set; }
internal MsQuicNativeMethods.StreamOpenDelegate StreamOpenDelegate { get; private set; }
internal MsQuicNativeMethods.StreamCloseDelegate StreamCloseDelegate { get; private set; }
internal MsQuicNativeMethods.StreamStartDelegate StreamStartDelegate { get; private set; }
internal MsQuicNativeMethods.StreamShutdownDelegate StreamShutdownDelegate { get; private set; }
internal MsQuicNativeMethods.StreamSendDelegate StreamSendDelegate { get; private set; }
internal MsQuicNativeMethods.StreamReceiveCompleteDelegate StreamReceiveComplete { get; private set; }
internal MsQuicNativeMethods.SetContextDelegate SetContextDelegate { get; private set; }
internal MsQuicNativeMethods.GetContextDelegate GetContextDelegate { get; private set; }
internal MsQuicNativeMethods.SetCallbackHandlerDelegate SetCallbackHandlerDelegate { get; private set; }
internal MsQuicNativeMethods.SetParamDelegate SetParamDelegate { get; private set; }
internal MsQuicNativeMethods.GetParamDelegate GetParamDelegate { get; private set; }
internal void RegistrationOpen(byte[] name)
{
MsQuicStatusException.ThrowIfFailed(RegistrationOpenDelegate(name, out var ctx));
_registrationContext = ctx;
}
internal unsafe uint UnsafeSetParam(
IntPtr Handle,
uint Level,
uint Param,
MsQuicNativeMethods.QuicBuffer Buffer)
{
return SetParamDelegate(
Handle,
Level,
Param,
Buffer.Length,
Buffer.Buffer);
}
public void Dispose()
{
Dispose(disposing: true);
GC.SuppressFinalize(this);
}
~MsQuicApi()
{
Dispose(disposing: false);
}
private void Dispose(bool disposing)
{
if (_disposed)
{
return;
}
RegistrationCloseDelegate?.Invoke(_registrationContext);
_disposed = true;
}
}
}

View File

@ -0,0 +1,148 @@
// 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;
namespace Microsoft.AspNetCore.Server.Kestrel.Transport.MsQuic.Internal
{
internal static class MsQuicConstants
{
private const uint Success = 0;
internal static Func<uint, string> ErrorTypeFromErrorCode = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? Windows.GetError : (Func<uint, string>)Linux.GetError;
internal static class Windows
{
internal const uint Pending = 0x703E5;
internal const uint Continue = 0x704DE;
internal const uint OutOfMemory = 0x8007000E;
internal const uint InvalidParameter = 0x80070057;
internal const uint InvalidState = 0x8007139F;
internal const uint NotSupported = 0x80004002;
internal const uint NotFound = 0x80070490;
internal const uint BufferTooSmall = 0x8007007A;
internal const uint HandshakeFailure = 0x80410000;
internal const uint Aborted = 0x80004004;
internal const uint AddressInUse = 0x80072740;
internal const uint ConnectionTimeout = 0x800704CF;
internal const uint ConnectionIdle = 0x800704D4;
internal const uint InternalError = 0x80004005;
internal const uint ServerBusy = 0x800704C9;
internal const uint ProtocolError = 0x800704CD;
internal const uint VerNegError = 0x80410001;
// TODO return better error messages here.
public static string GetError(uint status)
{
switch (status)
{
case Success:
return "SUCCESS";
case Pending:
return "PENDING";
case Continue:
return "CONTINUE";
case OutOfMemory:
return "OUT_OF_MEMORY";
case InvalidParameter:
return "INVALID_PARAMETER";
case InvalidState:
return "INVALID_STATE";
case NotSupported:
return "NOT_SUPPORTED";
case NotFound:
return "NOT_FOUND";
case BufferTooSmall:
return "BUFFER_TOO_SMALL";
case HandshakeFailure:
return "HANDSHAKE_FAILURE";
case Aborted:
return "ABORTED";
case AddressInUse:
return "ADDRESS_IN_USE";
case ConnectionTimeout:
return "CONNECTION_TIMEOUT";
case ConnectionIdle:
return "CONNECTION_IDLE";
case InternalError:
return "INTERNAL_ERROR";
case ServerBusy:
return "SERVER_BUSY";
case ProtocolError:
return "PROTOCOL_ERROR";
case VerNegError:
return "VER_NEG_ERROR";
}
return status.ToString();
}
}
internal static class Linux
{
internal const uint Pending = unchecked((uint)-2);
internal const uint Continue = unchecked((uint)-1);
internal const uint OutOfMemory = 12;
internal const uint InvalidParameter = 22;
internal const uint InvalidState = 200000002;
internal const uint NotSupported = 95;
internal const uint NotFound = 2;
internal const uint BufferTooSmall = 75;
internal const uint HandshakeFailure = 200000009;
internal const uint Aborted = 200000008;
internal const uint AddressInUse = 98;
internal const uint ConnectionTimeout = 110;
internal const uint ConnectionIdle = 200000011;
internal const uint InternalError = 200000012;
internal const uint ServerBusy = 200000007;
internal const uint ProtocolError = 200000013;
internal const uint VerNegError = 200000014;
public static string GetError(uint status)
{
switch (status)
{
case Success:
return "SUCCESS";
case Pending:
return "PENDING";
case Continue:
return "CONTINUE";
case OutOfMemory:
return "OUT_OF_MEMORY";
case InvalidParameter:
return "INVALID_PARAMETER";
case InvalidState:
return "INVALID_STATE";
case NotSupported:
return "NOT_SUPPORTED";
case NotFound:
return "NOT_FOUND";
case BufferTooSmall:
return "BUFFER_TOO_SMALL";
case HandshakeFailure:
return "HANDSHAKE_FAILURE";
case Aborted:
return "ABORTED";
case AddressInUse:
return "ADDRESS_IN_USE";
case ConnectionTimeout:
return "CONNECTION_TIMEOUT";
case ConnectionIdle:
return "CONNECTION_IDLE";
case InternalError:
return "INTERNAL_ERROR";
case ServerBusy:
return "SERVER_BUSY";
case ProtocolError:
return "PROTOCOL_ERROR";
case VerNegError:
return "VER_NEG_ERROR";
}
return status.ToString();
}
}
}
}

View File

@ -0,0 +1,178 @@
// 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
{
/// <summary>
/// Flags to pass when creating a certificate hash store.
/// </summary>
[Flags]
internal enum QUIC_CERT_HASH_STORE_FLAG : uint
{
NONE = 0,
MACHINE_CERT = 0x0001,
}
/// <summary>
/// Flags to pass when creating a security config.
/// </summary>
[Flags]
internal enum QUIC_SEC_CONFIG_FLAG : uint
{
NONE = 0,
CERT_HASH = 0x00000001,
CERT_HASH_STORE = 0x00000002,
CERT_CONTEXT = 0x00000004,
CERT_FILE = 0x00000008,
ENABL_OCSP = 0x00000010,
CERT_NULL = 0xF0000000,
}
internal enum QUIC_LISTENER_EVENT : byte
{
NEW_CONNECTION = 0
}
internal enum QUIC_CONNECTION_EVENT : byte
{
CONNECTED = 0,
SHUTDOWN_BEGIN = 1,
SHUTDOWN_BEGIN_PEER = 2,
SHUTDOWN_COMPLETE = 3,
LOCAL_ADDR_CHANGED = 4,
PEER_ADDR_CHANGED = 5,
NEW_STREAM = 6,
STREAMS_AVAILABLE = 7,
PEER_NEEDS_STREAMS = 8,
IDEAL_SEND_BUFFER = 9,
}
[Flags]
internal enum QUIC_CONNECTION_SHUTDOWN_FLAG : uint
{
NONE = 0x0,
SILENT = 0x1
}
internal enum QUIC_PARAM_LEVEL : uint
{
REGISTRATION = 0,
SESSION = 1,
LISTENER = 2,
CONNECTION = 3,
TLS = 4,
STREAM = 5,
}
internal enum QUIC_PARAM_REGISTRATION : uint
{
RETRY_MEMORY_PERCENT = 0,
CID_PREFIX = 1
}
internal enum QUIC_PARAM_SESSION : uint
{
TLS_TICKET_KEY = 0,
PEER_BIDI_STREAM_COUNT = 1,
PEER_UNIDI_STREAM_COUNT = 2,
IDLE_TIMEOUT = 3,
DISCONNECT_TIMEOUT = 4,
MAX_BYTES_PER_KEY = 5
}
internal enum QUIC_PARAM_LISTENER : uint
{
LOCAL_ADDRESS = 0
}
internal enum QUIC_PARAM_CONN : uint
{
QUIC_VERSION = 0,
LOCAL_ADDRESS = 1,
REMOTE_ADDRESS = 2,
IDLE_TIMEOUT = 3,
PEER_BIDI_STREAM_COUNT = 4,
PEER_UNIDI_STREAM_COUNT = 5,
LOCAL_BIDI_STREAM_COUNT = 6,
LOCAL_UNIDI_STREAM_COUNT = 7,
CLOSE_REASON_PHRASE = 8,
STATISTICS = 9,
STATISTICS_PLAT = 10,
CERT_VALIDATION_FLAGS = 11,
KEEP_ALIVE_ENABLED = 12,
DISCONNECT_TIMEOUT = 13,
SEC_CONFIG = 14,
USE_SEND_BUFFER = 15,
USE_PACING = 16,
SHARE_UDP_BINDING = 17,
IDEAL_PROCESSOR = 18,
MAX_STREAM_IDS = 19
}
internal enum QUIC_PARAM_STREAM : uint
{
ID = 0,
RECEIVE_ENABLED = 1,
ZERORTT_LENGTH = 2,
IDEAL_SEND_BUFFER = 3
}
internal enum QUIC_STREAM_EVENT : byte
{
START_COMPLETE = 0,
RECV = 1,
SEND_COMPLETE = 2,
PEER_SEND_CLOSE = 3,
PEER_SEND_ABORT = 4,
PEER_RECV_ABORT = 5,
SEND_SHUTDOWN_COMPLETE = 6,
SHUTDOWN_COMPLETE = 7,
IDEAL_SEND_BUFFER_SIZE = 8,
}
[Flags]
internal enum QUIC_STREAM_OPEN_FLAG : uint
{
NONE = 0,
UNIDIRECTIONAL = 0x1,
ZERO_RTT = 0x2,
}
[Flags]
internal enum QUIC_STREAM_START_FLAG : uint
{
NONE = 0,
FAIL_BLOCKED = 0x1,
IMMEDIATE = 0x2,
ASYNC = 0x4,
}
[Flags]
internal enum QUIC_STREAM_SHUTDOWN_FLAG : uint
{
NONE = 0,
GRACEFUL = 0x1,
ABORT_SEND = 0x2,
ABORT_RECV = 0x4,
ABORT = 0x6,
IMMEDIATE = 0x8
}
[Flags]
internal enum QUIC_SEND_FLAG : uint
{
NONE = 0,
ALLOW_0_RTT = 0x00000001,
FIN = 0x00000002,
}
[Flags]
internal enum QUIC_RECV_FLAG : byte
{
NONE = 0,
ZERO_RTT = 0x1,
FIN = 0x02
}
}

View File

@ -0,0 +1,540 @@
// 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.Runtime.InteropServices;
using System.Text;
namespace Microsoft.AspNetCore.Server.Kestrel.Transport.MsQuic.Internal
{
/// <summary>
/// Contains all native delegates and structs that are used with MsQuic.
/// </summary>
internal unsafe static class MsQuicNativeMethods
{
internal const string dllName = "msquic.dll";
[DllImport(dllName)]
internal static extern int MsQuicOpen(int version, out NativeApi* registration);
[StructLayout(LayoutKind.Sequential)]
internal struct NativeApi
{
internal uint Version;
internal IntPtr SetContext;
internal IntPtr GetContext;
internal IntPtr SetCallbackHandler;
internal IntPtr SetParam;
internal IntPtr GetParam;
internal IntPtr RegistrationOpen;
internal IntPtr RegistrationClose;
internal IntPtr SecConfigCreate;
internal IntPtr SecConfigDelete;
internal IntPtr SessionOpen;
internal IntPtr SessionClose;
internal IntPtr SessionShutdown;
internal IntPtr ListenerOpen;
internal IntPtr ListenerClose;
internal IntPtr ListenerStart;
internal IntPtr ListenerStop;
internal IntPtr ConnectionOpen;
internal IntPtr ConnectionClose;
internal IntPtr ConnectionShutdown;
internal IntPtr ConnectionStart;
internal IntPtr StreamOpen;
internal IntPtr StreamClose;
internal IntPtr StreamStart;
internal IntPtr StreamShutdown;
internal IntPtr StreamSend;
internal IntPtr StreamReceiveComplete;
}
internal delegate uint SetContextDelegate(
IntPtr Handle,
IntPtr Context);
internal delegate IntPtr GetContextDelegate(
IntPtr Handle);
internal delegate void SetCallbackHandlerDelegate(
IntPtr Handle,
IntPtr Handler,
IntPtr Context);
internal delegate uint SetParamDelegate(
IntPtr Handle,
uint Level,
uint Param,
uint BufferLength,
byte* Buffer);
internal delegate uint GetParamDelegate(
IntPtr Handle,
uint Level,
uint Param,
IntPtr BufferLength,
IntPtr Buffer);
internal delegate uint RegistrationOpenDelegate(byte[] appName, out IntPtr RegistrationContext);
internal delegate void RegistrationCloseDelegate(IntPtr RegistrationContext);
[StructLayout(LayoutKind.Sequential)]
internal struct CertHash
{
internal const int ShaHashLength = 20;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = ShaHashLength)]
internal byte[] ShaHash;
}
[StructLayout(LayoutKind.Sequential)]
internal struct CertHashStore
{
internal const int ShaHashLength = 20;
internal const int StoreNameLength = 128;
internal uint Flags;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = ShaHashLength)]
internal byte[] ShaHash;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = StoreNameLength)]
internal byte[] StoreName;
}
[StructLayout(LayoutKind.Sequential)]
internal struct CertFile
{
[MarshalAs(UnmanagedType.ByValArray)]
internal byte[] ShaHashUtf8;
[MarshalAs(UnmanagedType.ByValArray)]
internal byte[] StoreNameUtf8;
}
internal delegate void SecConfigCreateCompleteDelegate(IntPtr Context, uint Status, IntPtr SecurityConfig);
internal delegate uint SecConfigCreateDelegate(
IntPtr RegistrationContext,
uint Flags,
IntPtr Certificate,
[MarshalAs(UnmanagedType.LPStr)]string Principal,
IntPtr Context,
SecConfigCreateCompleteDelegate CompletionHandler);
internal delegate void SecConfigDeleteDelegate(
IntPtr SecurityConfig);
internal delegate uint SessionOpenDelegate(
IntPtr RegistrationContext,
byte[] utf8String,
IntPtr Context,
ref IntPtr Session);
internal delegate void SessionCloseDelegate(
IntPtr Session);
internal delegate void SessionShutdownDelegate(
IntPtr Session,
uint Flags,
ushort ErrorCode);
[StructLayout(LayoutKind.Sequential)]
internal struct ListenerEvent
{
internal QUIC_LISTENER_EVENT Type;
internal ListenerEventDataUnion Data;
}
[StructLayout(LayoutKind.Explicit)]
internal struct ListenerEventDataUnion
{
[FieldOffset(0)]
internal ListenerEventDataNewConnection NewConnection;
}
[StructLayout(LayoutKind.Sequential)]
internal struct ListenerEventDataNewConnection
{
internal IntPtr Info;
internal IntPtr Connection;
internal IntPtr SecurityConfig;
internal static string BufferToString(IntPtr buffer, ushort bufferLength)
{
if (bufferLength == 0)
{
return "";
}
var utf8Bytes = new byte[bufferLength]; // TODO: Avoid extra alloc and copy.
Marshal.Copy(buffer, utf8Bytes, 0, bufferLength);
var str = Encoding.UTF8.GetString(utf8Bytes);
return str;
}
}
[StructLayout(LayoutKind.Sequential)]
internal struct NewConnectionInfo
{
internal uint QuicVersion;
internal IntPtr LocalAddress;
internal IntPtr RemoteAddress;
internal ushort CryptoBufferLength;
internal ushort AlpnListLength;
internal ushort ServerNameLength;
internal IntPtr CryptoBuffer;
internal IntPtr AlpnList;
internal IntPtr ServerName;
}
internal delegate uint ListenerCallbackDelegate(
IntPtr listener,
IntPtr context,
ref ListenerEvent evt);
internal delegate uint ListenerOpenDelegate(
IntPtr session,
ListenerCallbackDelegate handler,
IntPtr context,
out IntPtr listener);
internal delegate uint ListenerCloseDelegate(
IntPtr listener);
internal delegate uint ListenerStartDelegate(
IntPtr listener,
ref SOCKADDR_INET localAddress);
internal delegate uint ListenerStopDelegate(
IntPtr listener);
[StructLayout(LayoutKind.Sequential)]
internal struct ConnectionEventDataConnected
{
internal bool EarlyDataAccepted;
}
[StructLayout(LayoutKind.Sequential)]
internal struct ConnectionEventDataShutdownBegin
{
internal uint Status;
}
[StructLayout(LayoutKind.Sequential)]
internal struct ConnectionEventDataShutdownBeginPeer
{
internal ushort ErrorCode;
}
[StructLayout(LayoutKind.Sequential)]
internal struct ConnectionEventDataShutdownComplete
{
internal bool TimedOut;
}
[StructLayout(LayoutKind.Sequential)]
internal struct ConnectionEventDataLocalAddrChanged
{
internal IntPtr Address; // TODO this needs to be IPV4 and IPV6
}
[StructLayout(LayoutKind.Sequential)]
internal struct ConnectionEventDataPeerAddrChanged
{
internal IntPtr Address; // TODO this needs to be IPV4 and IPV6
}
[StructLayout(LayoutKind.Sequential)]
internal struct ConnectionEventDataNewStream
{
internal IntPtr Stream;
internal QUIC_STREAM_OPEN_FLAG Flags;
}
[StructLayout(LayoutKind.Sequential)]
internal struct ConnectionEventDataStreamsAvailable
{
internal ushort BiDirectionalCount;
internal ushort UniDirectionalCount;
}
[StructLayout(LayoutKind.Sequential)]
internal struct ConnectionEventDataIdealSendBuffer
{
internal ulong NumBytes;
}
[StructLayout(LayoutKind.Explicit)]
internal struct ConnectionEventDataUnion
{
[FieldOffset(0)]
internal ConnectionEventDataConnected Connected;
[FieldOffset(0)]
internal ConnectionEventDataShutdownBegin ShutdownBegin;
[FieldOffset(0)]
internal ConnectionEventDataShutdownBeginPeer ShutdownBeginPeer;
[FieldOffset(0)]
internal ConnectionEventDataShutdownComplete ShutdownComplete;
[FieldOffset(0)]
internal ConnectionEventDataLocalAddrChanged LocalAddrChanged;
[FieldOffset(0)]
internal ConnectionEventDataPeerAddrChanged PeerAddrChanged;
[FieldOffset(0)]
internal ConnectionEventDataNewStream NewStream;
[FieldOffset(0)]
internal ConnectionEventDataStreamsAvailable StreamsAvailable;
[FieldOffset(0)]
internal ConnectionEventDataIdealSendBuffer IdealSendBuffer;
}
[StructLayout(LayoutKind.Sequential)]
internal struct ConnectionEvent
{
internal QUIC_CONNECTION_EVENT Type;
internal ConnectionEventDataUnion Data;
internal bool EarlyDataAccepted => Data.Connected.EarlyDataAccepted;
internal ulong NumBytes => Data.IdealSendBuffer.NumBytes;
internal IPEndPoint LocalAddress => null; // TODO
internal IPEndPoint PeerAddress => null; // TODO
internal uint ShutdownBeginStatus => Data.ShutdownBegin.Status;
internal ushort ShutdownBeginPeerStatus => Data.ShutdownBeginPeer.ErrorCode;
internal bool ShutdownTimedOut => Data.ShutdownComplete.TimedOut;
internal ushort BiDirectionalCount => Data.StreamsAvailable.BiDirectionalCount;
internal ushort UniDirectionalCount => Data.StreamsAvailable.UniDirectionalCount;
internal QUIC_STREAM_OPEN_FLAG StreamFlags => Data.NewStream.Flags;
}
internal delegate uint ConnectionCallbackDelegate(
IntPtr Connection,
IntPtr Context,
ref ConnectionEvent Event);
internal delegate uint ConnectionOpenDelegate(
IntPtr Session,
ConnectionCallbackDelegate Handler,
IntPtr Context,
out IntPtr Connection);
internal delegate uint ConnectionCloseDelegate(
IntPtr Connection);
internal delegate uint ConnectionStartDelegate(
IntPtr Connection,
ushort Family,
[MarshalAs(UnmanagedType.LPStr)]
string ServerName,
ushort ServerPort);
internal delegate uint ConnectionShutdownDelegate(
IntPtr Connection,
uint Flags,
ushort ErrorCode);
[StructLayout(LayoutKind.Sequential)]
internal struct StreamEventDataRecv
{
internal ulong AbsoluteOffset;
internal ulong TotalBufferLength;
internal QuicBuffer* Buffers;
internal uint BufferCount;
internal byte Flags;
}
[StructLayout(LayoutKind.Explicit)]
internal struct StreamEventDataSendComplete
{
[FieldOffset(7)]
internal byte Canceled;
[FieldOffset(8)]
internal IntPtr ClientContext;
internal bool IsCanceled()
{
return Canceled != 0;
}
}
[StructLayout(LayoutKind.Sequential)]
internal struct StreamEventDataPeerSendAbort
{
internal ushort ErrorCode;
}
[StructLayout(LayoutKind.Sequential)]
internal struct StreamEventDataPeerRecvAbort
{
internal ushort ErrorCode;
}
[StructLayout(LayoutKind.Sequential)]
internal struct StreamEventDataSendShutdownComplete
{
internal bool Graceful;
}
[StructLayout(LayoutKind.Explicit)]
internal struct StreamEventDataUnion
{
[FieldOffset(0)]
internal StreamEventDataRecv Recv;
[FieldOffset(0)]
internal StreamEventDataSendComplete SendComplete;
[FieldOffset(0)]
internal StreamEventDataPeerSendAbort PeerSendAbort;
[FieldOffset(0)]
internal StreamEventDataPeerRecvAbort PeerRecvAbort;
[FieldOffset(0)]
internal StreamEventDataSendShutdownComplete SendShutdownComplete;
}
[StructLayout(LayoutKind.Sequential)]
internal struct StreamEvent
{
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)]
internal struct SOCKADDR_IN
{
internal ushort sin_family;
internal ushort sin_port;
internal byte sin_addr0;
internal byte sin_addr1;
internal byte sin_addr2;
internal byte sin_addr3;
internal byte[] Address
{
get
{
return new byte[] { sin_addr0, sin_addr1, sin_addr2, sin_addr3 };
}
}
}
[StructLayout(LayoutKind.Sequential)]
internal struct SOCKADDR_IN6
{
internal ushort sin6_family;
internal ushort sin6_port;
internal uint sin6_flowinfo;
internal byte sin6_addr0;
internal byte sin6_addr1;
internal byte sin6_addr2;
internal byte sin6_addr3;
internal byte sin6_addr4;
internal byte sin6_addr5;
internal byte sin6_addr6;
internal byte sin6_addr7;
internal byte sin6_addr8;
internal byte sin6_addr9;
internal byte sin6_addr10;
internal byte sin6_addr11;
internal byte sin6_addr12;
internal byte sin6_addr13;
internal byte sin6_addr14;
internal byte sin6_addr15;
internal uint sin6_scope_id;
internal byte[] Address
{
get
{
return new byte[] {
sin6_addr0, sin6_addr1, sin6_addr2, sin6_addr3 ,
sin6_addr4, sin6_addr5, sin6_addr6, sin6_addr7 ,
sin6_addr8, sin6_addr9, sin6_addr10, sin6_addr11 ,
sin6_addr12, sin6_addr13, sin6_addr14, sin6_addr15 };
}
}
}
[StructLayout(LayoutKind.Explicit, CharSet = CharSet.Ansi)]
internal struct SOCKADDR_INET
{
[FieldOffset(0)]
internal SOCKADDR_IN Ipv4;
[FieldOffset(0)]
internal SOCKADDR_IN6 Ipv6;
[FieldOffset(0)]
internal ushort si_family;
}
}
}

View File

@ -0,0 +1,42 @@
// 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 MsQuicStatusException : Exception
{
internal MsQuicStatusException(uint status)
: this(status, null)
{
}
internal MsQuicStatusException(uint status, string message)
: this(status, message, null)
{
}
internal MsQuicStatusException(uint status, string message, Exception innerException)
: base(GetMessage(status, message), innerException)
{
Status = status;
}
internal uint Status { get; }
private static string GetMessage(uint status, string message)
{
var errorCode = MsQuicConstants.ErrorTypeFromErrorCode(status);
return $"Quic Error: {errorCode}. " + message;
}
internal static void ThrowIfFailed(uint status, string message = null, Exception innerException = null)
{
if (!MsQuicStatusHelper.Succeeded(status))
{
throw new MsQuicStatusException(status, message, innerException);
}
}
}
}

View File

@ -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 MsQuicStatusHelper
{
internal static bool Succeeded(uint status)
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
return status < 0x80000000;
}
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
{
return (int)status <= 0;
}
return false;
}
}
}

View File

@ -0,0 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Description>Libuv transport for the ASP.NET Core Kestrel cross-platform web server.</Description>
<TargetFramework>$(DefaultNetCoreTargetFramework)</TargetFramework>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<PackageTags>aspnetcore;kestrel</PackageTags>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<NoWarn>CS1591;$(NoWarn)</NoWarn>
<IsShippingPackage>false</IsShippingPackage>
</PropertyGroup>
<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>
</Project>