@@ -95,6 +95,73 @@ void ConfigureListenOptions(ListenOptions listenOptions)
9595 }
9696 }
9797
98+ [ ConditionalFact ]
99+ [ OSSkipCondition ( OperatingSystems . MacOSX , SkipReason = "Missing SslStream ALPN support: https://github.com/dotnet/corefx/issues/30492" ) ]
100+ [ MinimumOSVersion ( OperatingSystems . Windows , WindowsVersions . Win81 ) ]
101+ public async Task CanUseDefaultSniConfiguration ( )
102+ {
103+ var configuration = new ConfigurationBuilder ( ) . AddInMemoryCollection ( new Dictionary < string , string >
104+ {
105+ [ "EndpointDefaults:Sni:*.example.org:Protocols" ] = "Http1" ,
106+ [ "EndpointDefaults:Sni:dot.net:Protocols" ] = "None" ,
107+ } ) . Build ( ) ;
108+
109+ var options = new KestrelServerOptions ( ) ;
110+ var env = new Mock < IHostEnvironment > ( ) ;
111+ env . SetupGet ( e => e . ContentRootPath ) . Returns ( Directory . GetCurrentDirectory ( ) ) ;
112+
113+ options . ApplicationServices = new ServiceCollection ( )
114+ . AddLogging ( )
115+ . AddSingleton ( env . Object )
116+ . BuildServiceProvider ( ) ;
117+
118+ options . Configure ( configuration ) . Load ( ) ;
119+
120+ void ConfigureListenOptions ( ListenOptions listenOptions )
121+ {
122+ listenOptions . KestrelServerOptions = options ;
123+ listenOptions . UseHttps ( _x509Certificate2 ) ;
124+ // We don't need to set listenOptions.Protocols since it will be flowed through the HttpProtocolsFeature
125+ } ;
126+
127+ await using var server = new TestServer ( context => Task . CompletedTask , new TestServiceContext ( LoggerFactory ) , ConfigureListenOptions ) ;
128+
129+ using var exampleConnection = server . CreateConnection ( ) ;
130+ var exampleSslOptions = new SslClientAuthenticationOptions
131+ {
132+ TargetHost = "a.example.org" ,
133+ ApplicationProtocols = new List < SslApplicationProtocol > { SslApplicationProtocol . Http11 , SslApplicationProtocol . Http2 } ,
134+ } ;
135+
136+ using var exampleStream = OpenSslStream ( exampleConnection . Stream ) ;
137+ await exampleStream . AuthenticateAsClientAsync ( exampleSslOptions ) ;
138+
139+ Assert . Equal ( SslApplicationProtocol . Http11 , exampleStream . NegotiatedApplicationProtocol ) ;
140+
141+ using var dotnetConnection = server . CreateConnection ( ) ;
142+ var dotnetSslOptions = new SslClientAuthenticationOptions
143+ {
144+ TargetHost = "dot.net" ,
145+ ApplicationProtocols = new List < SslApplicationProtocol > { SslApplicationProtocol . Http11 , SslApplicationProtocol . Http2 } ,
146+ } ;
147+
148+ using var dotnetStream = OpenSslStream ( dotnetConnection . Stream ) ;
149+ await dotnetStream . AuthenticateAsClientAsync ( dotnetSslOptions ) ;
150+
151+ // HttpProtocols.None was configured, so there is no negotiated protocol
152+ Assert . True ( dotnetStream . NegotiatedApplicationProtocol . Protocol . IsEmpty ) ;
153+
154+ using var emptyNameConnection = server . CreateConnection ( ) ;
155+ var emptyNameSslOptions = new SslClientAuthenticationOptions
156+ {
157+ TargetHost = "" ,
158+ } ;
159+
160+ using var refusedStream = OpenSslStream ( emptyNameConnection . Stream ) ;
161+ // We expect the handshake to throw here because Kestrel refuses the connection due to there being no TargetHost and now wildcard config.
162+ await Assert . ThrowsAsync < IOException > ( async ( ) => await refusedStream . AuthenticateAsClientAsync ( emptyNameSslOptions ) ) ;
163+ }
164+
98165 [ Fact ]
99166 public async Task HandshakeDetailsAreAvailable ( )
100167 {
0 commit comments