1
1
// Licensed to the .NET Foundation under one or more agreements.
2
2
// The .NET Foundation licenses this file to you under the MIT license.
3
3
4
+ using System . Diagnostics ;
4
5
using System . Diagnostics . CodeAnalysis ;
5
6
using System . Runtime . InteropServices ;
6
7
using Microsoft . Quic ;
@@ -47,7 +48,8 @@ private MsQuicApi(QUIC_API_TABLE* apiTable)
47
48
}
48
49
}
49
50
50
- internal static MsQuicApi Api { get ; } = null ! ;
51
+ private static readonly Lazy < MsQuicApi > _api = new Lazy < MsQuicApi > ( AllocateMsQuicApi ) ;
52
+ internal static MsQuicApi Api => _api . Value ;
51
53
52
54
internal static bool IsQuicSupported { get ; }
53
55
@@ -58,29 +60,21 @@ private MsQuicApi(QUIC_API_TABLE* apiTable)
58
60
59
61
static MsQuicApi ( )
60
62
{
61
- IntPtr msQuicHandle ;
62
- if ( ! NativeLibrary . TryLoad ( $ "{ Interop . Libraries . MsQuic } .{ MsQuicVersion . Major } ", typeof ( MsQuicApi ) . Assembly , DllImportSearchPath . AssemblyDirectory , out msQuicHandle ) &&
63
- ! NativeLibrary . TryLoad ( Interop . Libraries . MsQuic , typeof ( MsQuicApi ) . Assembly , DllImportSearchPath . AssemblyDirectory , out msQuicHandle ) )
63
+ if ( ! TryLoadMsQuic ( out IntPtr msQuicHandle ) )
64
64
{
65
65
return ;
66
66
}
67
67
68
68
try
69
69
{
70
- if ( ! NativeLibrary . TryGetExport ( msQuicHandle , "MsQuicOpenVersion" , out IntPtr msQuicOpenVersionAddress ) )
71
- {
72
- return ;
73
- }
74
-
75
- QUIC_API_TABLE * apiTable = null ;
76
- delegate * unmanaged[ Cdecl] < uint , QUIC_API_TABLE * * , int > msQuicOpenVersion = ( delegate * unmanaged[ Cdecl] < uint , QUIC_API_TABLE * * , int > ) msQuicOpenVersionAddress ;
77
- if ( StatusFailed ( msQuicOpenVersion ( ( uint ) MsQuicVersion . Major , & apiTable ) ) )
70
+ if ( ! TryOpenMsQuic ( msQuicHandle , out QUIC_API_TABLE * apiTable , out _ ) )
78
71
{
79
72
return ;
80
73
}
81
74
82
75
try
83
76
{
77
+ // Check version
84
78
int arraySize = 4 ;
85
79
uint * libVersion = stackalloc uint [ arraySize ] ;
86
80
uint size = ( uint ) arraySize * sizeof ( uint ) ;
@@ -99,7 +93,7 @@ static MsQuicApi()
99
93
return ;
100
94
}
101
95
102
- // Assume SChannel is being used on windows and query for the actual provider from the library
96
+ // Assume SChannel is being used on windows and query for the actual provider from the library if querying is supported
103
97
QUIC_TLS_PROVIDER provider = OperatingSystem . IsWindows ( ) ? QUIC_TLS_PROVIDER . SCHANNEL : QUIC_TLS_PROVIDER . OPENSSL ;
104
98
size = sizeof ( QUIC_TLS_PROVIDER ) ;
105
99
apiTable ->GetParam ( null , QUIC_PARAM_GLOBAL_TLS_PROVIDER , & size , & provider ) ;
@@ -122,26 +116,84 @@ static MsQuicApi()
122
116
Tls13ClientMayBeDisabled = IsTls13Disabled ( isServer : false ) ;
123
117
}
124
118
125
- Api = new MsQuicApi ( apiTable ) ;
126
119
IsQuicSupported = true ;
127
120
}
128
121
finally
129
122
{
130
- if ( ! IsQuicSupported && NativeLibrary . TryGetExport ( msQuicHandle , "MsQuicClose" , out IntPtr msQuicClose ) )
131
- {
132
- // Gracefully close the API table
133
- ( ( delegate * unmanaged[ Cdecl] < QUIC_API_TABLE * , void > ) msQuicClose ) ( apiTable ) ;
134
- }
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" ) ;
135
126
}
136
-
137
127
}
138
128
finally
139
129
{
140
- if ( ! IsQuicSupported )
130
+ // Unload the library, we will load it again when we actually use QUIC
131
+ NativeLibrary . Free ( msQuicHandle ) ;
132
+ }
133
+ }
134
+
135
+ private static MsQuicApi AllocateMsQuicApi ( )
136
+ {
137
+ Debug . Assert ( IsQuicSupported ) ;
138
+
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 ) )
143
+ {
144
+ return new MsQuicApi ( apiTable ) ;
145
+ }
146
+
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" ) ;
151
+ }
152
+
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 )
158
+ {
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
+ }
170
+
171
+ 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 ) ;
174
+ if ( StatusFailed ( openStatus ) )
175
+ {
176
+ if ( NetEventSource . Log . IsEnabled ( ) )
141
177
{
142
- NativeLibrary . Free ( msQuicHandle ) ;
178
+ NetEventSource . Info ( null , $ "MsQuicOpenVersion returned { openStatus } status code." ) ;
143
179
}
180
+
181
+ return false ;
182
+ }
183
+
184
+ apiTable = table ;
185
+ return true ;
186
+ }
187
+
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 ;
144
194
}
195
+
196
+ return false ;
145
197
}
146
198
147
199
private static bool IsWindowsVersionSupported ( ) => OperatingSystem . IsWindowsVersionAtLeast ( MinWindowsVersion . Major ,
0 commit comments