-
Notifications
You must be signed in to change notification settings - Fork 5.3k
Don't unload MsQuic from the process #75441
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
Changes from 2 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -20,6 +20,8 @@ internal sealed unsafe partial class MsQuicApi | |
|
|
||
| private static readonly Version MsQuicVersion = new Version(2, 1); | ||
|
|
||
| private static readonly IntPtr MsQuicHandle = TryLoadMsQuic(out IntPtr handle) ? handle : IntPtr.Zero; | ||
|
|
||
| public MsQuicSafeHandle Registration { get; } | ||
|
|
||
| public QUIC_API_TABLE* ApiTable { get; } | ||
|
|
@@ -60,104 +62,93 @@ private MsQuicApi(QUIC_API_TABLE* apiTable) | |
|
|
||
| static MsQuicApi() | ||
| { | ||
| if (!TryLoadMsQuic(out IntPtr msQuicHandle)) | ||
| if (MsQuicHandle == IntPtr.Zero) | ||
| { | ||
| // MsQuic library not loaded | ||
| return; | ||
| } | ||
|
|
||
| if (!TryOpenMsQuic(out QUIC_API_TABLE* apiTable, out _)) | ||
| { | ||
| return; | ||
| } | ||
|
|
||
| try | ||
| { | ||
| if (!TryOpenMsQuic(msQuicHandle, out QUIC_API_TABLE* apiTable, out _)) | ||
| // Check version | ||
| int arraySize = 4; | ||
| uint* libVersion = stackalloc uint[arraySize]; | ||
| uint size = (uint)arraySize * sizeof(uint); | ||
| if (StatusFailed(apiTable->GetParam(null, QUIC_PARAM_GLOBAL_LIBRARY_VERSION, &size, libVersion))) | ||
| { | ||
| return; | ||
| } | ||
|
|
||
| try | ||
| var version = new Version((int)libVersion[0], (int)libVersion[1], (int)libVersion[2], (int)libVersion[3]); | ||
| if (version < MsQuicVersion) | ||
| { | ||
| // Check version | ||
| int arraySize = 4; | ||
| uint* libVersion = stackalloc uint[arraySize]; | ||
| uint size = (uint)arraySize * sizeof(uint); | ||
| if (StatusFailed(apiTable->GetParam(null, QUIC_PARAM_GLOBAL_LIBRARY_VERSION, &size, libVersion))) | ||
| if (NetEventSource.Log.IsEnabled()) | ||
| { | ||
| return; | ||
| } | ||
|
|
||
| var version = new Version((int)libVersion[0], (int)libVersion[1], (int)libVersion[2], (int)libVersion[3]); | ||
| if (version < MsQuicVersion) | ||
| { | ||
| if (NetEventSource.Log.IsEnabled()) | ||
| { | ||
| NetEventSource.Info(null, $"Incompatible MsQuic library version '{version}', expecting '{MsQuicVersion}'"); | ||
| } | ||
| return; | ||
| NetEventSource.Info(null, $"Incompatible MsQuic library version '{version}', expecting '{MsQuicVersion}'"); | ||
| } | ||
| return; | ||
| } | ||
|
|
||
| // Assume SChannel is being used on windows and query for the actual provider from the library if querying is supported | ||
| QUIC_TLS_PROVIDER provider = OperatingSystem.IsWindows() ? QUIC_TLS_PROVIDER.SCHANNEL : QUIC_TLS_PROVIDER.OPENSSL; | ||
| size = sizeof(QUIC_TLS_PROVIDER); | ||
| apiTable->GetParam(null, QUIC_PARAM_GLOBAL_TLS_PROVIDER, &size, &provider); | ||
| UsesSChannelBackend = provider == QUIC_TLS_PROVIDER.SCHANNEL; | ||
| // Assume SChannel is being used on windows and query for the actual provider from the library if querying is supported | ||
| QUIC_TLS_PROVIDER provider = OperatingSystem.IsWindows() ? QUIC_TLS_PROVIDER.SCHANNEL : QUIC_TLS_PROVIDER.OPENSSL; | ||
| size = sizeof(QUIC_TLS_PROVIDER); | ||
| apiTable->GetParam(null, QUIC_PARAM_GLOBAL_TLS_PROVIDER, &size, &provider); | ||
| UsesSChannelBackend = provider == QUIC_TLS_PROVIDER.SCHANNEL; | ||
|
|
||
| if (UsesSChannelBackend) | ||
| if (UsesSChannelBackend) | ||
| { | ||
| // Implies windows platform, check TLS1.3 availability | ||
| if (!IsWindowsVersionSupported()) | ||
| { | ||
| // Implies windows platform, check TLS1.3 availability | ||
| if (!IsWindowsVersionSupported()) | ||
| if (NetEventSource.Log.IsEnabled()) | ||
| { | ||
| if (NetEventSource.Log.IsEnabled()) | ||
| { | ||
| NetEventSource.Info(null, $"Current Windows version ({Environment.OSVersion}) is not supported by QUIC. Minimal supported version is {MinWindowsVersion}"); | ||
| } | ||
|
|
||
| return; | ||
| NetEventSource.Info(null, $"Current Windows version ({Environment.OSVersion}) is not supported by QUIC. Minimal supported version is {MinWindowsVersion}"); | ||
| } | ||
|
|
||
| Tls13ServerMayBeDisabled = IsTls13Disabled(isServer: true); | ||
| Tls13ClientMayBeDisabled = IsTls13Disabled(isServer: false); | ||
| return; | ||
| } | ||
|
|
||
| IsQuicSupported = true; | ||
| } | ||
| finally | ||
| { | ||
| // Gracefully close the API table to free resources. The API table will be allocated lazily again if needed | ||
| bool closed = TryCloseMsQuic(msQuicHandle, apiTable); | ||
| Debug.Assert(closed, "Failed to close MsQuic"); | ||
| Tls13ServerMayBeDisabled = IsTls13Disabled(isServer: true); | ||
| Tls13ClientMayBeDisabled = IsTls13Disabled(isServer: false); | ||
| } | ||
|
|
||
| IsQuicSupported = true; | ||
| } | ||
| finally | ||
| { | ||
| // Unload the library, we will load it again when we actually use QUIC | ||
| NativeLibrary.Free(msQuicHandle); | ||
| // Gracefully close the API table to free resources. The API table will be allocated lazily again if needed | ||
| bool closed = TryCloseMsQuic(apiTable); | ||
| Debug.Assert(closed, "Failed to close MsQuic"); | ||
| } | ||
| } | ||
|
|
||
| private static MsQuicApi AllocateMsQuicApi() | ||
| { | ||
| Debug.Assert(IsQuicSupported); | ||
|
|
||
| int openStatus = MsQuic.QUIC_STATUS_INTERNAL_ERROR; | ||
|
|
||
| if (TryLoadMsQuic(out IntPtr msQuicHandle) && | ||
| TryOpenMsQuic(msQuicHandle, out QUIC_API_TABLE* apiTable, out openStatus)) | ||
| if (!TryOpenMsQuic(out QUIC_API_TABLE* apiTable, out int openStatus)) | ||
| { | ||
| return new MsQuicApi(apiTable); | ||
| throw ThrowHelper.GetExceptionForMsQuicStatus(openStatus); | ||
| } | ||
|
|
||
| ThrowHelper.ThrowIfMsQuicError(openStatus); | ||
|
|
||
| // this should unreachable as TryOpenMsQuic returns non-success status on failure | ||
| throw new Exception("Failed to create MsQuicApi instance"); | ||
| return new MsQuicApi(apiTable); | ||
| } | ||
|
|
||
| private static bool TryLoadMsQuic(out IntPtr msQuicHandle) => | ||
| NativeLibrary.TryLoad($"{Interop.Libraries.MsQuic}.{MsQuicVersion.Major}", typeof(MsQuicApi).Assembly, DllImportSearchPath.AssemblyDirectory, out msQuicHandle) || | ||
| NativeLibrary.TryLoad(Interop.Libraries.MsQuic, typeof(MsQuicApi).Assembly, DllImportSearchPath.AssemblyDirectory, out msQuicHandle); | ||
|
|
||
| private static bool TryOpenMsQuic(IntPtr msQuicHandle, out QUIC_API_TABLE* apiTable, out int openStatus) | ||
| private static bool TryOpenMsQuic(out QUIC_API_TABLE* apiTable, out int openStatus) | ||
| { | ||
| Debug.Assert(MsQuicHandle != IntPtr.Zero); | ||
|
|
||
| apiTable = null; | ||
| if (!NativeLibrary.TryGetExport(msQuicHandle, "MsQuicOpenVersion", out IntPtr msQuicOpenVersionAddress)) | ||
| if (!NativeLibrary.TryGetExport(MsQuicHandle, "MsQuicOpenVersion", out IntPtr msQuicOpenVersionAddress)) | ||
|
||
| { | ||
| if (NetEventSource.Log.IsEnabled()) | ||
| { | ||
|
|
@@ -185,9 +176,11 @@ private static bool TryOpenMsQuic(IntPtr msQuicHandle, out QUIC_API_TABLE* apiTa | |
| return true; | ||
| } | ||
|
|
||
| private static bool TryCloseMsQuic(IntPtr msQuicHandle, QUIC_API_TABLE* apiTable) | ||
| private static bool TryCloseMsQuic(QUIC_API_TABLE* apiTable) | ||
|
||
| { | ||
| if (NativeLibrary.TryGetExport(msQuicHandle, "MsQuicClose", out IntPtr msQuicClose)) | ||
| Debug.Assert(MsQuicHandle != IntPtr.Zero); | ||
|
|
||
| if (NativeLibrary.TryGetExport(MsQuicHandle, "MsQuicClose", out IntPtr msQuicClose)) | ||
| { | ||
| ((delegate* unmanaged[Cdecl]<QUIC_API_TABLE*, void>)msQuicClose)(apiTable); | ||
| return true; | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
and delete inline initialization of MsQuicHandle above
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of storing MsQuicHandle, you can consider storing the pointers to the two exports in statics instead. It would save a bit of redundant work on the re-initialization.