Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MsQuic interop #1

Closed
wants to merge 13 commits into from
65 changes: 65 additions & 0 deletions src/libraries/Common/tests/TestUtilities/TestEventListener.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Collections.Generic;
using System.Diagnostics.Tracing;
using System.Text;
using Xunit.Abstractions;

namespace TestUtilities;

/// <summary>
/// Logging helper for tests.
/// Logs event source events into test output.
/// </summary>
public sealed class TestEventListener : EventListener
{
private readonly ITestOutputHelper _output;
private readonly HashSet<string> _sourceNames;

// Until https://github.com/dotnet/runtime/issues/63979 is solved.
private List<EventSource> _eventSources = new List<EventSource>();

public TestEventListener(ITestOutputHelper output, params string[] sourceNames)
{
_output = output;
_sourceNames = new HashSet<string>(sourceNames);
foreach (var eventSource in _eventSources)
{
OnEventSourceCreated(eventSource);
}
_eventSources = null;
}

protected override void OnEventSourceCreated(EventSource eventSource)
{
// We're called from base ctor, just save the event source for later initialization.
if (_sourceNames is null)
{
_eventSources.Add(eventSource);
return;
}

// Second pass called from our ctor, allow logging for specified source names.
if (_sourceNames.Contains(eventSource.Name))
{
EnableEvents(eventSource, EventLevel.LogAlways);
}
}

protected override void OnEventWritten(EventWrittenEventArgs eventData)
{
var sb = new StringBuilder().Append($"{eventData.TimeStamp:HH:mm:ss.fffffff}[{eventData.EventName}] ");
for (int i = 0; i < eventData.Payload?.Count; i++)
{
if (i > 0)
sb.Append(", ");
sb.Append(eventData.PayloadNames?[i]).Append(": ").Append(eventData.Payload[i]);
}
try
{
_output.WriteLine(sb.ToString());
}
catch { }
}
}
2 changes: 2 additions & 0 deletions src/libraries/Common/tests/TestUtilities/TestUtilities.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@

<Compile Include="RandomTestCaseOrderer.cs" />
<Compile Include="RandomTestCollectionOrderer.cs" />

<Compile Include="TestEventListener.cs" />
</ItemGroup>
<!-- Windows imports -->
<ItemGroup>
Expand Down
25 changes: 3 additions & 22 deletions src/libraries/System.Net.Quic/src/System.Net.Quic.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -30,29 +30,16 @@
<Compile Include="System\Net\Quic\Implementations\Mock\*.cs" />
<Compile Include="System\Net\Quic\Implementations\MsQuic\*.cs" />
<Compile Include="System\Net\Quic\Implementations\MsQuic\Internal\*.cs" />
<Compile Include="System\Net\Quic\Implementations\MsQuic\Interop\MsQuicAlpnHelper.cs" />
<Compile Include="System\Net\Quic\Implementations\MsQuic\Interop\MsQuicEnums.cs" />
<Compile Include="System\Net\Quic\Implementations\MsQuic\Interop\MsQuicNativeMethods.cs" />
<Compile Include="System\Net\Quic\Implementations\MsQuic\Interop\MsQuicStatusCodes.cs" />
<Compile Include="System\Net\Quic\Implementations\MsQuic\Interop\MsQuicStatusHelper.cs" />
<Compile Include="System\Net\Quic\Implementations\MsQuic\Interop\MsQuicTraceHelper.cs" />
<Compile Include="System\Net\Quic\Implementations\MsQuic\Interop\SafeMsQuicConfigurationHandle.cs" />
<Compile Include="System\Net\Quic\Implementations\MsQuic\Interop\SafeMsQuicConnectionHandle.cs" />
<Compile Include="System\Net\Quic\Implementations\MsQuic\Interop\SafeMsQuicListenerHandle.cs" />
<Compile Include="System\Net\Quic\Implementations\MsQuic\Interop\SafeMsQuicRegistrationHandle.cs" />
<Compile Include="System\Net\Quic\Implementations\MsQuic\Interop\SafeMsQuicStreamHandle.cs" />
<Compile Include="$(CommonPath)DisableRuntimeMarshalling.cs">
<Link>Common\DisableRuntimeMarshalling.cs</Link>
</Compile>
<Compile Include="System\Net\Quic\Implementations\MsQuic\Interop\*.cs" />
<!-- System.Net common -->
<Compile Include="$(CommonPath)DisableRuntimeMarshalling.cs" Link="Common\DisableRuntimeMarshalling.cs" />
<Compile Include="$(CommonPath)System\Threading\Tasks\TaskToApm.cs" Link="Common\System\Threading\Tasks\TaskToApm.cs" />
<Compile Include="$(CommonPath)System\Net\ArrayBuffer.cs" Link="Common\System\Net\ArrayBuffer.cs" />
<Compile Include="$(CommonPath)System\Net\MultiArrayBuffer.cs" Link="Common\System\Net\MultiArrayBuffer.cs" />
<Compile Include="$(CommonPath)System\Net\Logging\NetEventSource.Common.cs" Link="Common\System\Net\Logging\NetEventSource.Common.cs" />
<Compile Include="$(CommonPath)System\Net\StreamBuffer.cs" Link="Common\System\Net\StreamBuffer.cs" />
<Compile Include="$(CommonPath)System\Net\SocketAddress.cs" Link="Common\System\Net\SocketAddress.cs" />
<Compile Include="$(CommonPath)System\Net\IPAddressParserStatics.cs" Link="Common\System\Net\IPAddressParserStatics.cs" />
<!-- System.Net.Internals -->
<Compile Include="$(CommonPath)System\Net\Internals\IPEndPointExtensions.cs" Link="Common\System\Net\Internals\IPEndPointExtensions.cs" />
</ItemGroup>
<!-- Unsupported platforms -->
Expand All @@ -75,7 +62,6 @@
<Compile Include="$(CommonPath)Interop\Windows\SChannel\Interop.SECURITY_STATUS.cs" Link="Common\Interop\Windows\SChannel\Interop.SECURITY_STATUS.cs" />
<Compile Include="$(CommonPath)System\Net\Security\CertificateValidation.Windows.cs" Link="Common\System\Net\Security\CertificateValidation.Windows.cs" />
<Compile Include="$(CommonPath)System\Net\SocketAddressPal.Windows.cs" Link="Common\System\Net\SocketAddressPal.Windows.cs" />
<Compile Include="System\Net\Quic\Implementations\MsQuic\Interop\MsQuicStatusCodes.Windows.cs" />
</ItemGroup>
<!-- Unix (OSX + Linux) specific files -->
<ItemGroup Condition="'$(TargetPlatformIdentifier)' == 'Linux' or '$(TargetPlatformIdentifier)' == 'OSX' or '$(TargetPlatformIdentifier)' == 'FreeBSD'">
Expand Down Expand Up @@ -108,26 +94,21 @@
</ItemGroup>
<!-- Linux specific files -->
<ItemGroup Condition="'$(TargetPlatformIdentifier)' == 'Linux'">
<Compile Include="$(CommonPath)Interop\Linux\Interop.Libraries.cs" Link="Common\Interop\Linux\Interop.Libraries.cs" />
<Compile Include="System\Net\Quic\Implementations\MsQuic\Interop\MsQuicStatusCodes.Linux.cs" />
<Compile Include="$(CommonPath)System\Net\Security\CertificateValidation.Unix.cs" Link="Common\System\Net\Security\CertificateValidation.Unix.cs" />
<Compile Include="$(CommonPath)Interop\Linux\Interop.Libraries.cs" Link="Common\Interop\Linux\Interop.Libraries.cs" />
</ItemGroup>
<!-- FreeBSD specific files -->
<ItemGroup Condition="'$(TargetPlatformIdentifier)' == 'FreeBSD' ">
<Compile Include="$(CommonPath)System\Net\Security\CertificateValidation.Unix.cs" Link="Common\System\Net\Security\CertificateValidation.Unix.cs" />
<Compile Include="$(CommonPath)Interop\FreeBSD\Interop.Libraries.cs" Link="Common\Interop\FreeBSD\Interop.Libraries.cs" />
<!-- Assume similarity with OSX for now -->
<Compile Include="System\Net\Quic\Implementations\MsQuic\Interop\MsQuicStatusCodes.OSX.cs" />
</ItemGroup>
<!-- OSX specific files -->
<ItemGroup Condition="'$(TargetPlatformIdentifier)' == 'OSX'">
<Compile Include="$(CommonPath)System\Net\Security\CertificateValidation.OSX.cs" Link="Common\System\Net\Security\CertificateValidation.OSX.cs" />
<Compile Include="$(CommonPath)Interop\OSX\Interop.Libraries.cs" Link="Common\Interop\OSX\Interop.Libraries.cs" />
<Compile Include="System\Net\Quic\Implementations\MsQuic\Interop\MsQuicStatusCodes.OSX.cs" />
</ItemGroup>

<!-- Project references -->

<ItemGroup>
<PackageReference Include="System.Net.MsQuic.Transport"
Version="$(SystemNetMsQuicTransportVersion)"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Net.Sockets;
using System.Runtime.InteropServices;
using static System.Net.Quic.Implementations.MsQuic.Internal.MsQuicNativeMethods;

namespace System.Net.Quic.Implementations.MsQuic.Internal
{
internal static class MsQuicAddressHelpers
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,16 @@
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Net.Security;
using static System.Net.Quic.Implementations.MsQuic.Internal.MsQuicNativeMethods;
using Microsoft.Quic;

namespace System.Net.Quic.Implementations.MsQuic.Internal
{
internal static class MsQuicAlpnHelper
{
public static unsafe void Prepare(List<SslApplicationProtocol> alpnProtocols, [NotNull] out MemoryHandle[]? handles, [NotNull] out QuicBuffer[]? buffers)
public static unsafe void Prepare(List<SslApplicationProtocol> alpnProtocols, [NotNull] out MemoryHandle[]? handles, [NotNull] out QUIC_BUFFER[]? buffers)
{
handles = ArrayPool<MemoryHandle>.Shared.Rent(alpnProtocols.Count);
buffers = ArrayPool<QuicBuffer>.Shared.Rent(alpnProtocols.Count);
buffers = ArrayPool<QUIC_BUFFER>.Shared.Rent(alpnProtocols.Count);

try
{
Expand All @@ -35,7 +35,7 @@ public static unsafe void Prepare(List<SslApplicationProtocol> alpnProtocols, [N
}
}

public static void Return(ref MemoryHandle[]? handles, ref QuicBuffer[]? buffers)
public static void Return(ref MemoryHandle[]? handles, ref QUIC_BUFFER[]? buffers)
{
if (handles is MemoryHandle[] notNullHandles)
{
Expand All @@ -48,10 +48,10 @@ public static void Return(ref MemoryHandle[]? handles, ref QuicBuffer[]? buffers
ArrayPool<MemoryHandle>.Shared.Return(notNullHandles);
}

if (buffers is QuicBuffer[] notNullBuffers)
if (buffers is QUIC_BUFFER[] notNullBuffers)
{
buffers = null;
ArrayPool<QuicBuffer>.Shared.Return(notNullBuffers);
ArrayPool<QUIC_BUFFER>.Shared.Return(notNullBuffers);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,27 @@

using System.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices;
using System.Text;
using Microsoft.Quic;

using static Microsoft.Quic.MsQuic;

#if TARGET_WINDOWS
using Microsoft.Win32;
#endif

using static System.Net.Quic.Implementations.MsQuic.Internal.MsQuicNativeMethods;

namespace System.Net.Quic.Implementations.MsQuic.Internal
{
internal unsafe sealed class MsQuicApi
{
private static readonly byte[] s_appName = Encoding.ASCII.GetBytes("System.Net.Quic");

private static readonly Version MinWindowsVersion = new Version(10, 0, 20145, 1000);

public SafeMsQuicRegistrationHandle Registration { get; }

public QUIC_API_TABLE* ApiTable { get; }

// This is workaround for a bug in ILTrimmer.
// Without these DynamicDependency attributes, .ctor() will be removed from the safe handles.
// Remove once fixed: https://github.com/mono/linker/issues/1660
Expand All @@ -26,81 +32,22 @@ internal unsafe sealed class MsQuicApi
[DynamicDependency(DynamicallyAccessedMemberTypes.PublicConstructors, typeof(SafeMsQuicListenerHandle))]
[DynamicDependency(DynamicallyAccessedMemberTypes.PublicConstructors, typeof(SafeMsQuicConnectionHandle))]
[DynamicDependency(DynamicallyAccessedMemberTypes.PublicConstructors, typeof(SafeMsQuicStreamHandle))]
private MsQuicApi(NativeApi* vtable)
private MsQuicApi(QUIC_API_TABLE* apiTable)
{
uint status;

SetParamDelegate =
new SetParamDelegate(new DelegateHelper(vtable->SetParam).SetParam);

GetParamDelegate =
new GetParamDelegate(new DelegateHelper(vtable->GetParam).GetParam);

SetCallbackHandlerDelegate =
new SetCallbackHandlerDelegate(new DelegateHelper(vtable->SetCallbackHandler).SetCallbackHandler);

RegistrationOpenDelegate =
new RegistrationOpenDelegate(new DelegateHelper(vtable->RegistrationOpen).RegistrationOpen);
RegistrationCloseDelegate =
Marshal.GetDelegateForFunctionPointer<RegistrationCloseDelegate>(
vtable->RegistrationClose);

ConfigurationOpenDelegate =
new ConfigurationOpenDelegate(new DelegateHelper(vtable->ConfigurationOpen).ConfigurationOpen);
ConfigurationCloseDelegate =
Marshal.GetDelegateForFunctionPointer<ConfigurationCloseDelegate>(
vtable->ConfigurationClose);
ConfigurationLoadCredentialDelegate =
new ConfigurationLoadCredentialDelegate(new DelegateHelper(vtable->ConfigurationLoadCredential).ConfigurationLoadCredential);

ListenerOpenDelegate =
new ListenerOpenDelegate(new DelegateHelper(vtable->ListenerOpen).ListenerOpen);
ListenerCloseDelegate =
Marshal.GetDelegateForFunctionPointer<ListenerCloseDelegate>(
vtable->ListenerClose);
ListenerStartDelegate =
new ListenerStartDelegate(new DelegateHelper(vtable->ListenerStart).ListenerStart);
ListenerStopDelegate =
new ListenerStopDelegate(new DelegateHelper(vtable->ListenerStop).ListenerStop);

ConnectionOpenDelegate =
new ConnectionOpenDelegate(new DelegateHelper(vtable->ConnectionOpen).ConnectionOpen);
ConnectionCloseDelegate =
Marshal.GetDelegateForFunctionPointer<ConnectionCloseDelegate>(
vtable->ConnectionClose);
ConnectionSetConfigurationDelegate =
new ConnectionSetConfigurationDelegate(new DelegateHelper(vtable->ConnectionSetConfiguration).ConnectionSetConfiguration);
ConnectionShutdownDelegate =
new ConnectionShutdownDelegate(new DelegateHelper(vtable->ConnectionShutdown).ConnectionShutdown);
ConnectionStartDelegate =
new ConnectionStartDelegate(new DelegateHelper(vtable->ConnectionStart).ConnectionStart);

StreamOpenDelegate =
new StreamOpenDelegate(new DelegateHelper(vtable->StreamOpen).StreamOpen);
StreamCloseDelegate =
Marshal.GetDelegateForFunctionPointer<StreamCloseDelegate>(
vtable->StreamClose);
StreamStartDelegate =
new StreamStartDelegate(new DelegateHelper(vtable->StreamStart).StreamStart);
StreamShutdownDelegate =
new StreamShutdownDelegate(new DelegateHelper(vtable->StreamShutdown).StreamShutdown);
StreamSendDelegate =
new StreamSendDelegate(new DelegateHelper(vtable->StreamSend).StreamSend);
StreamReceiveCompleteDelegate =
new StreamReceiveCompleteDelegate(new DelegateHelper(vtable->StreamReceiveComplete).StreamReceiveComplete);
StreamReceiveSetEnabledDelegate =
new StreamReceiveSetEnabledDelegate(new DelegateHelper(vtable->StreamReceiveSetEnabled).StreamReceiveSetEnabled);

var cfg = new RegistrationConfig
ApiTable = apiTable;

fixed (byte* pAppName = s_appName)
{
AppName = ".NET",
ExecutionProfile = QUIC_EXECUTION_PROFILE.QUIC_EXECUTION_PROFILE_LOW_LATENCY
};
var cfg = new QUIC_REGISTRATION_CONFIG {
AppName = (sbyte*)pAppName,
ExecutionProfile = QUIC_EXECUTION_PROFILE.QUIC_EXECUTION_PROFILE_LOW_LATENCY
};

status = RegistrationOpenDelegate(ref cfg, out SafeMsQuicRegistrationHandle handle);
QuicExceptionHelpers.ThrowIfFailed(status, "RegistrationOpen failed.");
QUIC_HANDLE* handle;
ThrowIfFailure(ApiTable->RegistrationOpen(&cfg, &handle), "RegistrationOpen failed");

Registration = handle;
Registration = new SafeMsQuicRegistrationHandle(handle);
}
}

internal static MsQuicApi Api { get; } = null!;
Expand Down Expand Up @@ -136,14 +83,13 @@ static MsQuicApi()
{
if (NativeLibrary.TryGetExport(msQuicHandle, "MsQuicOpenVersion", out IntPtr msQuicOpenVersionAddress))
{
NativeApi* vtable;
delegate* unmanaged[Cdecl]<uint, NativeApi**, uint> msQuicOpenVersion =
(delegate* unmanaged[Cdecl]<uint, NativeApi**, uint>)msQuicOpenVersionAddress;
uint status = msQuicOpenVersion(MsQuicVersion, &vtable);
if (MsQuicStatusHelper.SuccessfulStatusCode(status))
QUIC_API_TABLE* apiTable;
delegate* unmanaged[Cdecl]<uint, QUIC_API_TABLE**, int> msQuicOpenVersion = (delegate* unmanaged[Cdecl]<uint, QUIC_API_TABLE**, int>)msQuicOpenVersionAddress;
int status = msQuicOpenVersion(MsQuicVersion, &apiTable);
if (StatusSucceeded(status))
{
IsQuicSupported = true;
Api = new MsQuicApi(vtable);
Api = new MsQuicApi(apiTable);
}
}
}
Expand Down Expand Up @@ -182,38 +128,5 @@ private static bool IsTls13Disabled()
#endif
return false;
}

// TODO: Consider updating all of these delegates to instead use function pointers.
internal RegistrationOpenDelegate RegistrationOpenDelegate { get; }
internal RegistrationCloseDelegate RegistrationCloseDelegate { get; }

internal ConfigurationOpenDelegate ConfigurationOpenDelegate { get; }
internal ConfigurationCloseDelegate ConfigurationCloseDelegate { get; }
internal ConfigurationLoadCredentialDelegate ConfigurationLoadCredentialDelegate { get; }

internal ListenerOpenDelegate ListenerOpenDelegate { get; }
internal ListenerCloseDelegate ListenerCloseDelegate { get; }
internal ListenerStartDelegate ListenerStartDelegate { get; }
internal ListenerStopDelegate ListenerStopDelegate { get; }

// TODO: missing SendResumptionTicket
internal ConnectionOpenDelegate ConnectionOpenDelegate { get; }
internal ConnectionCloseDelegate ConnectionCloseDelegate { get; }
internal ConnectionShutdownDelegate ConnectionShutdownDelegate { get; }
internal ConnectionStartDelegate ConnectionStartDelegate { get; }
internal ConnectionSetConfigurationDelegate ConnectionSetConfigurationDelegate { get; }

internal StreamOpenDelegate StreamOpenDelegate { get; }
internal StreamCloseDelegate StreamCloseDelegate { get; }
internal StreamStartDelegate StreamStartDelegate { get; }
internal StreamShutdownDelegate StreamShutdownDelegate { get; }
internal StreamSendDelegate StreamSendDelegate { get; }
internal StreamReceiveCompleteDelegate StreamReceiveCompleteDelegate { get; }
internal StreamReceiveSetEnabledDelegate StreamReceiveSetEnabledDelegate { get; }

internal SetCallbackHandlerDelegate SetCallbackHandlerDelegate { get; }

internal SetParamDelegate SetParamDelegate { get; }
internal GetParamDelegate GetParamDelegate { get; }
}
}
Loading