@@ -20,6 +20,9 @@ internal sealed unsafe partial class MsQuicApi
2020
2121 private static readonly Version MsQuicVersion = new Version ( 2 , 1 ) ;
2222
23+ private static readonly delegate * unmanaged[ Cdecl] < uint , QUIC_API_TABLE * * , int > MsQuicOpenVersion ;
24+ private static readonly delegate * unmanaged[ Cdecl] < QUIC_API_TABLE * , void > MsQuicClose ;
25+
2326 public MsQuicSafeHandle Registration { get ; }
2427
2528 public QUIC_API_TABLE * ApiTable { get ; }
@@ -58,144 +61,112 @@ private MsQuicApi(QUIC_API_TABLE* apiTable)
5861 internal static bool Tls13ServerMayBeDisabled { get ; }
5962 internal static bool Tls13ClientMayBeDisabled { get ; }
6063
64+ #pragma warning disable CA1810 // Initialize all static fields in 'MsQuicApi' when those fields are declared and remove the explicit static constructor
6165 static MsQuicApi ( )
6266 {
63- if ( ! TryLoadMsQuic ( out IntPtr msQuicHandle ) )
67+ if ( ! NativeLibrary . TryLoad ( $ "{ Interop . Libraries . MsQuic } .{ MsQuicVersion . Major } ", typeof ( MsQuicApi ) . Assembly , DllImportSearchPath . AssemblyDirectory , out IntPtr msQuicHandle ) &&
68+ ! NativeLibrary . TryLoad ( Interop . Libraries . MsQuic , typeof ( MsQuicApi ) . Assembly , DllImportSearchPath . AssemblyDirectory , out msQuicHandle ) )
69+ {
70+ // MsQuic library not loaded
71+ return ;
72+ }
73+
74+ MsQuicOpenVersion = ( delegate * unmanaged[ Cdecl] < uint , QUIC_API_TABLE * * , int > ) NativeLibrary . GetExport ( msQuicHandle , nameof ( MsQuicOpenVersion ) ) ;
75+ MsQuicClose = ( delegate * unmanaged[ Cdecl] < QUIC_API_TABLE * , void > ) NativeLibrary . GetExport ( msQuicHandle , nameof ( MsQuicClose ) ) ;
76+
77+ if ( ! TryOpenMsQuic ( out QUIC_API_TABLE * apiTable , out _ ) )
6478 {
79+ // Too low version of the library (likely pre-2.0)
6580 return ;
6681 }
6782
6883 try
6984 {
70- if ( ! TryOpenMsQuic ( msQuicHandle , out QUIC_API_TABLE * apiTable , out _ ) )
85+ // Check version
86+ int arraySize = 4 ;
87+ uint * libVersion = stackalloc uint [ arraySize ] ;
88+ uint size = ( uint ) arraySize * sizeof ( uint ) ;
89+ if ( StatusFailed ( apiTable ->GetParam ( null , QUIC_PARAM_GLOBAL_LIBRARY_VERSION , & size , libVersion ) ) )
7190 {
7291 return ;
7392 }
7493
75- try
94+ var version = new Version ( ( int ) libVersion [ 0 ] , ( int ) libVersion [ 1 ] , ( int ) libVersion [ 2 ] , ( int ) libVersion [ 3 ] ) ;
95+ if ( version < MsQuicVersion )
7696 {
77- // Check version
78- int arraySize = 4 ;
79- uint * libVersion = stackalloc uint [ arraySize ] ;
80- uint size = ( uint ) arraySize * sizeof ( uint ) ;
81- if ( StatusFailed ( apiTable ->GetParam ( null , QUIC_PARAM_GLOBAL_LIBRARY_VERSION , & size , libVersion ) ) )
97+ if ( NetEventSource . Log . IsEnabled ( ) )
8298 {
83- return ;
84- }
85-
86- var version = new Version ( ( int ) libVersion [ 0 ] , ( int ) libVersion [ 1 ] , ( int ) libVersion [ 2 ] , ( int ) libVersion [ 3 ] ) ;
87- if ( version < MsQuicVersion )
88- {
89- if ( NetEventSource . Log . IsEnabled ( ) )
90- {
91- NetEventSource . Info ( null , $ "Incompatible MsQuic library version '{ version } ', expecting '{ MsQuicVersion } '") ;
92- }
93- return ;
99+ NetEventSource . Info ( null , $ "Incompatible MsQuic library version '{ version } ', expecting '{ MsQuicVersion } '") ;
94100 }
101+ return ;
102+ }
95103
96- // Assume SChannel is being used on windows and query for the actual provider from the library if querying is supported
97- QUIC_TLS_PROVIDER provider = OperatingSystem . IsWindows ( ) ? QUIC_TLS_PROVIDER . SCHANNEL : QUIC_TLS_PROVIDER . OPENSSL ;
98- size = sizeof ( QUIC_TLS_PROVIDER ) ;
99- apiTable ->GetParam ( null , QUIC_PARAM_GLOBAL_TLS_PROVIDER , & size , & provider ) ;
100- UsesSChannelBackend = provider == QUIC_TLS_PROVIDER . SCHANNEL ;
104+ // Assume SChannel is being used on windows and query for the actual provider from the library if querying is supported
105+ QUIC_TLS_PROVIDER provider = OperatingSystem . IsWindows ( ) ? QUIC_TLS_PROVIDER . SCHANNEL : QUIC_TLS_PROVIDER . OPENSSL ;
106+ size = sizeof ( QUIC_TLS_PROVIDER ) ;
107+ apiTable ->GetParam ( null , QUIC_PARAM_GLOBAL_TLS_PROVIDER , & size , & provider ) ;
108+ UsesSChannelBackend = provider == QUIC_TLS_PROVIDER . SCHANNEL ;
101109
102- if ( UsesSChannelBackend )
110+ if ( UsesSChannelBackend )
111+ {
112+ // Implies windows platform, check TLS1.3 availability
113+ if ( ! IsWindowsVersionSupported ( ) )
103114 {
104- // Implies windows platform, check TLS1.3 availability
105- if ( ! IsWindowsVersionSupported ( ) )
115+ if ( NetEventSource . Log . IsEnabled ( ) )
106116 {
107- if ( NetEventSource . Log . IsEnabled ( ) )
108- {
109- NetEventSource . Info ( null , $ "Current Windows version ({ Environment . OSVersion } ) is not supported by QUIC. Minimal supported version is { MinWindowsVersion } ") ;
110- }
111-
112- return ;
117+ NetEventSource . Info ( null , $ "Current Windows version ({ Environment . OSVersion } ) is not supported by QUIC. Minimal supported version is { MinWindowsVersion } ") ;
113118 }
114119
115- Tls13ServerMayBeDisabled = IsTls13Disabled ( isServer : true ) ;
116- Tls13ClientMayBeDisabled = IsTls13Disabled ( isServer : false ) ;
120+ return ;
117121 }
118122
119- IsQuicSupported = true ;
120- }
121- finally
122- {
123- // Gracefully close the API table to free resources. The API table will be allocated lazily again if needed
124- bool closed = TryCloseMsQuic ( msQuicHandle , apiTable ) ;
125- Debug . Assert ( closed , "Failed to close MsQuic" ) ;
123+ Tls13ServerMayBeDisabled = IsTls13Disabled ( isServer : true ) ;
124+ Tls13ClientMayBeDisabled = IsTls13Disabled ( isServer : false ) ;
126125 }
126+
127+ IsQuicSupported = true ;
127128 }
128129 finally
129130 {
130- // Unload the library, we will load it again when we actually use QUIC
131- NativeLibrary . Free ( msQuicHandle ) ;
131+ // Gracefully close the API table to free resources. The API table will be allocated lazily again if needed
132+ MsQuicClose ( apiTable ) ;
132133 }
133134 }
135+ #pragma warning restore CA1810
134136
135137 private static MsQuicApi AllocateMsQuicApi ( )
136138 {
137139 Debug . Assert ( IsQuicSupported ) ;
138140
139- int openStatus = MsQuic . QUIC_STATUS_INTERNAL_ERROR ;
140-
141- if ( TryLoadMsQuic ( out IntPtr msQuicHandle ) &&
142- TryOpenMsQuic ( msQuicHandle , out QUIC_API_TABLE * apiTable , out openStatus ) )
141+ if ( ! TryOpenMsQuic ( out QUIC_API_TABLE * apiTable , out int openStatus ) )
143142 {
144- return new MsQuicApi ( apiTable ) ;
143+ throw ThrowHelper . GetExceptionForMsQuicStatus ( openStatus ) ;
145144 }
146145
147- ThrowHelper . ThrowIfMsQuicError ( openStatus ) ;
148-
149- // this should unreachable as TryOpenMsQuic returns non-success status on failure
150- throw new Exception ( "Failed to create MsQuicApi instance" ) ;
146+ return new MsQuicApi ( apiTable ) ;
151147 }
152148
153- private static bool TryLoadMsQuic ( out IntPtr msQuicHandle ) =>
154- NativeLibrary . TryLoad ( $ "{ Interop . Libraries . MsQuic } .{ MsQuicVersion . Major } ", typeof ( MsQuicApi ) . Assembly , DllImportSearchPath . AssemblyDirectory , out msQuicHandle ) ||
155- NativeLibrary . TryLoad ( Interop . Libraries . MsQuic , typeof ( MsQuicApi ) . Assembly , DllImportSearchPath . AssemblyDirectory , out msQuicHandle ) ;
156-
157- private static bool TryOpenMsQuic ( IntPtr msQuicHandle , out QUIC_API_TABLE * apiTable , out int openStatus )
149+ private static bool TryOpenMsQuic ( out QUIC_API_TABLE * apiTable , out int openStatus )
158150 {
159- apiTable = null ;
160- if ( ! NativeLibrary . TryGetExport ( msQuicHandle , "MsQuicOpenVersion" , out IntPtr msQuicOpenVersionAddress ) )
161- {
162- if ( NetEventSource . Log . IsEnabled ( ) )
163- {
164- NetEventSource . Info ( null , "Failed to get MsQuicOpenVersion export in msquic library." ) ;
165- }
166-
167- openStatus = MsQuic . QUIC_STATUS_NOT_FOUND ;
168- return false ;
169- }
151+ Debug . Assert ( MsQuicOpenVersion != null ) ;
170152
171153 QUIC_API_TABLE * table = null ;
172- delegate * unmanaged[ Cdecl] < uint , QUIC_API_TABLE * * , int > msQuicOpenVersion = ( delegate * unmanaged[ Cdecl] < uint , QUIC_API_TABLE * * , int > ) msQuicOpenVersionAddress ;
173- openStatus = msQuicOpenVersion ( ( uint ) MsQuicVersion . Major , & table ) ;
154+ openStatus = MsQuicOpenVersion ( ( uint ) MsQuicVersion . Major , & table ) ;
174155 if ( StatusFailed ( openStatus ) )
175156 {
176157 if ( NetEventSource . Log . IsEnabled ( ) )
177158 {
178159 NetEventSource . Info ( null , $ "MsQuicOpenVersion returned { openStatus } status code.") ;
179160 }
180161
162+ apiTable = null ;
181163 return false ;
182164 }
183165
184166 apiTable = table ;
185167 return true ;
186168 }
187169
188- private static bool TryCloseMsQuic ( IntPtr msQuicHandle , QUIC_API_TABLE * apiTable )
189- {
190- if ( NativeLibrary . TryGetExport ( msQuicHandle , "MsQuicClose" , out IntPtr msQuicClose ) )
191- {
192- ( ( delegate * unmanaged[ Cdecl] < QUIC_API_TABLE * , void > ) msQuicClose ) ( apiTable ) ;
193- return true ;
194- }
195-
196- return false ;
197- }
198-
199170 private static bool IsWindowsVersionSupported ( ) => OperatingSystem . IsWindowsVersionAtLeast ( MinWindowsVersion . Major ,
200171 MinWindowsVersion . Minor , MinWindowsVersion . Build , MinWindowsVersion . Revision ) ;
201172
0 commit comments