2121using System . Diagnostics ;
2222using System . Diagnostics . CodeAnalysis ;
2323using System . Globalization ;
24+ using System . Net . Http ;
2425using System . Net . Security ;
2526using System . Net . Sockets ;
2627using System . Reflection ;
@@ -78,6 +79,7 @@ public static async Task<int> Main(string[] args)
7879 var streamsOption = new Option < int > ( new string [ ] { "--streams" } , ( ) => 1 , "Maximum concurrent streams per connection" ) ;
7980 var enableCertAuthOption = new Option < bool > ( new string [ ] { "--enableCertAuth" } , ( ) => false , "Flag indicating whether client sends a client certificate" ) ;
8081 var deadlineOption = new Option < int > ( new string [ ] { "--deadline" } , "Duration of deadline in seconds" ) ;
82+ var winHttpHandlerOption = new Option < bool > ( new string [ ] { "--winhttphandler" } , ( ) => false , "Whether to use WinHttpHandler with Grpc.Net.Client" ) ;
8183
8284 var rootCommand = new RootCommand ( ) ;
8385 rootCommand . AddOption ( urlOption ) ;
@@ -97,6 +99,7 @@ public static async Task<int> Main(string[] args)
9799 rootCommand . AddOption ( streamsOption ) ;
98100 rootCommand . AddOption ( enableCertAuthOption ) ;
99101 rootCommand . AddOption ( deadlineOption ) ;
102+ rootCommand . AddOption ( winHttpHandlerOption ) ;
100103
101104 rootCommand . SetHandler ( async ( InvocationContext context ) =>
102105 {
@@ -118,6 +121,7 @@ public static async Task<int> Main(string[] args)
118121 _options . Streams = context . ParseResult . GetValueForOption ( streamsOption ) ;
119122 _options . EnableCertAuth = context . ParseResult . GetValueForOption ( enableCertAuthOption ) ;
120123 _options . Deadline = context . ParseResult . GetValueForOption ( deadlineOption ) ;
124+ _options . WinHttpHandler = context . ParseResult . GetValueForOption ( winHttpHandlerOption ) ;
121125
122126 var runtimeVersion = typeof ( object ) . GetTypeInfo ( ) . Assembly . GetCustomAttribute < AssemblyInformationalVersionAttribute > ( ) ? . InformationalVersion ?? "Unknown" ;
123127 var isServerGC = GCSettings . IsServerGC ;
@@ -159,8 +163,10 @@ public static async Task<int> Main(string[] args)
159163 return await rootCommand . InvokeAsync ( args ) ;
160164 }
161165
166+ #if NET9_0_OR_GREATER
162167 [ UnconditionalSuppressMessage ( "AotAnalysis" , "IL3050:RequiresDynamicCode" ,
163168 Justification = "DependencyInjection only used with safe types." ) ]
169+ #endif
164170 private static ILoggerFactory CreateLoggerFactory ( )
165171 {
166172 return LoggerFactory . Create ( c =>
@@ -244,7 +250,7 @@ private static async Task StartScenario()
244250 var text = "Exception from test: " + ex . Message ;
245251 Log ( text ) ;
246252 _errorStringBuilder . AppendLine ( ) ;
247- _errorStringBuilder . Append ( CultureInfo . InvariantCulture , $ "[{ DateTime . Now : hh:mm:ss.fff} ] { text } " ) ;
253+ _errorStringBuilder . Append ( string . Format ( CultureInfo . InvariantCulture , "[{0 :hh:mm:ss.fff}] {1}" , DateTime . Now , text ) ) ;
248254 }
249255 }
250256
@@ -425,7 +431,9 @@ private static void CreateChannels()
425431 var initialUri = _options . Url ! ;
426432 var resolvedUri = initialUri . Authority ;
427433
434+ Log ( $ "Framework version: { System . Runtime . InteropServices . RuntimeInformation . FrameworkDescription } ") ;
428435 Log ( $ "gRPC client type: { _options . GrpcClientType } ") ;
436+ Log ( $ "WinHttpHandler: { ( _options . GrpcClientType == GrpcClientType . GrpcNetClient ? _options . WinHttpHandler . ToString ( ) : "N/A" ) } ") ;
429437 Log ( $ "Log level: { _options . LogLevel } ") ;
430438 Log ( $ "Protocol: '{ _options . Protocol } '") ;
431439 Log ( $ "Creating channels to '{ resolvedUri } '") ;
@@ -465,43 +473,12 @@ private static ChannelBase CreateChannel(string target)
465473 var address = useTls ? "https://" : "http://" ;
466474 address += target ;
467475
468- var httpClientHandler = new SocketsHttpHandler ( ) ;
469- httpClientHandler . UseProxy = false ;
470- httpClientHandler . AllowAutoRedirect = false ;
471- if ( _options . EnableCertAuth )
472- {
473- var basePath = Path . GetDirectoryName ( AppContext . BaseDirectory ) ;
474- var certPath = Path . Combine ( basePath ! , "Certs" , "client.pfx" ) ;
475- var clientCertificates = X509CertificateLoader . LoadPkcs12CollectionFromFile ( certPath , "1111" ) ;
476- httpClientHandler . SslOptions . ClientCertificates = clientCertificates ;
477- }
478-
479- if ( ! string . IsNullOrEmpty ( _options . UdsFileName ) )
480- {
481- var connectionFactory = new UnixDomainSocketConnectionFactory ( new UnixDomainSocketEndPoint ( ResolveUdsPath ( _options . UdsFileName ) ) ) ;
482- httpClientHandler . ConnectCallback = connectionFactory . ConnectAsync ;
483- }
484- else if ( ! string . IsNullOrEmpty ( _options . NamedPipeName ) )
485- {
486- var connectionFactory = new NamedPipeConnectionFactory ( _options . NamedPipeName ) ;
487- httpClientHandler . ConnectCallback = connectionFactory . ConnectAsync ;
488- }
489-
490- httpClientHandler . SslOptions . RemoteCertificateValidationCallback =
491- ( object sender , X509Certificate ? certificate , X509Chain ? chain , SslPolicyErrors sslPolicyErrors ) => true ;
492-
493- HttpMessageHandler httpMessageHandler = httpClientHandler ;
494-
495- Version ? versionOverride = null ;
496- if ( _options . Protocol == "h3" )
497- {
498- // Stop gRPC channel from creating TCP socket.
499- httpClientHandler . ConnectCallback = ( context , cancellationToken ) => throw new InvalidOperationException ( "Should never be called for H3." ) ;
500-
501- // Force H3 on all requests.
502- versionOverride = new Version ( 3 , 0 ) ;
503- }
476+ // Force H3 on all requests.
477+ var versionOverride = _options . Protocol == "h3"
478+ ? new Version ( 3 , 0 )
479+ : null ;
504480
481+ var httpMessageHandler = CreateMessageHandler ( ) ;
505482 return GrpcChannel . ForAddress ( address , new GrpcChannelOptions
506483 {
507484 HttpHandler = httpMessageHandler ,
@@ -511,6 +488,61 @@ private static ChannelBase CreateChannel(string target)
511488 }
512489 }
513490
491+ private static HttpMessageHandler CreateMessageHandler ( )
492+ {
493+ if ( _options . WinHttpHandler )
494+ {
495+ return CreateWinHttpHandler ( ) ;
496+ }
497+
498+ #if NET9_0_OR_GREATER
499+ var httpClientHandler = new SocketsHttpHandler ( ) ;
500+ httpClientHandler . UseProxy = false ;
501+ httpClientHandler . AllowAutoRedirect = false ;
502+ if ( _options . EnableCertAuth )
503+ {
504+ var basePath = Path . GetDirectoryName ( AppContext . BaseDirectory ) ;
505+ var certPath = Path . Combine ( basePath ! , "Certs" , "client.pfx" ) ;
506+ var clientCertificates = X509CertificateLoader . LoadPkcs12CollectionFromFile ( certPath , "1111" ) ;
507+ httpClientHandler . SslOptions . ClientCertificates = clientCertificates ;
508+ }
509+
510+ if ( ! string . IsNullOrEmpty ( _options . UdsFileName ) )
511+ {
512+ var connectionFactory = new UnixDomainSocketConnectionFactory ( new UnixDomainSocketEndPoint ( ResolveUdsPath ( _options . UdsFileName ) ) ) ;
513+ httpClientHandler . ConnectCallback = connectionFactory . ConnectAsync ;
514+ }
515+ else if ( ! string . IsNullOrEmpty ( _options . NamedPipeName ) )
516+ {
517+ var connectionFactory = new NamedPipeConnectionFactory ( _options . NamedPipeName ) ;
518+ httpClientHandler . ConnectCallback = connectionFactory . ConnectAsync ;
519+ }
520+
521+ httpClientHandler . SslOptions . RemoteCertificateValidationCallback =
522+ ( object sender , X509Certificate ? certificate , X509Chain ? chain , SslPolicyErrors sslPolicyErrors ) => true ;
523+
524+ if ( _options . Protocol == "h3" )
525+ {
526+ // Stop gRPC channel from creating TCP socket.
527+ httpClientHandler . ConnectCallback = ( context , cancellationToken ) => throw new InvalidOperationException ( "Should never be called for H3." ) ;
528+ }
529+
530+ return httpClientHandler ;
531+ #else
532+ return CreateWinHttpHandler ( ) ;
533+ #endif
534+ }
535+
536+ private static WinHttpHandler CreateWinHttpHandler ( )
537+ {
538+ #pragma warning disable CA1416 // Validate platform compatibility
539+ return new WinHttpHandler
540+ {
541+ ServerCertificateValidationCallback = ( sender , certificate , chain , sslPolicyErrors ) => true ,
542+ } ;
543+ #pragma warning restore CA1416 // Validate platform compatibility
544+ }
545+
514546 private static string ResolveUdsPath ( string udsFileName ) => Path . Combine ( Path . GetTempPath ( ) , udsFileName ) ;
515547
516548 private static SslCredentials GetSslCredentials ( )
0 commit comments