@@ -431,6 +431,230 @@ public void FallsBackToHttpsConnectionAdapterServerCertificateSelectorOverServer
431431 Assert . Same ( selectorCertificate , options . ServerCertificate ) ;
432432 }
433433
434+ [ Fact ]
435+ public void PrefersHttpProtocolsDefinedInSniConfig ( )
436+ {
437+ var sniDictionary = new Dictionary < string , SniConfig >
438+ {
439+ {
440+ "www.example.org" ,
441+ new SniConfig
442+ {
443+ Protocols = HttpProtocols . None ,
444+ Certificate = new CertificateConfig ( )
445+ }
446+ }
447+ } ;
448+
449+ var sniOptionsSelector = new SniOptionsSelector (
450+ "TestEndpointName" ,
451+ sniDictionary ,
452+ new MockCertificateConfigLoader ( ) ,
453+ new HttpsConnectionAdapterOptions ( ) ,
454+ fallbackHttpProtocols : HttpProtocols . Http1 ,
455+ logger : Mock . Of < ILogger < HttpsConnectionMiddleware > > ( ) ) ;
456+
457+ var mockConnectionContext = new MockConnectionContext ( ) ;
458+ sniOptionsSelector . GetOptions ( mockConnectionContext , "www.example.org" ) ;
459+
460+ var httpProtocolsFeature = mockConnectionContext . Features . Get < HttpProtocolsFeature > ( ) ;
461+ Assert . NotNull ( httpProtocolsFeature ) ;
462+ Assert . Equal ( HttpProtocols . None , httpProtocolsFeature . HttpProtocols ) ;
463+ }
464+
465+ [ Fact ]
466+ public void ConfiguresAlpnBasedOnConfiguredHttpProtocols ( )
467+ {
468+ var sniDictionary = new Dictionary < string , SniConfig >
469+ {
470+ {
471+ "www.example.org" ,
472+ new SniConfig
473+ {
474+ // I'm not using Http1AndHttp2 or Http2 because I don't want to account for
475+ // validation and normalization. Other tests cover that.
476+ Protocols = HttpProtocols . Http1 ,
477+ Certificate = new CertificateConfig ( )
478+ }
479+ }
480+ } ;
481+
482+ var sniOptionsSelector = new SniOptionsSelector (
483+ "TestEndpointName" ,
484+ sniDictionary ,
485+ new MockCertificateConfigLoader ( ) ,
486+ new HttpsConnectionAdapterOptions ( ) ,
487+ fallbackHttpProtocols : HttpProtocols . None ,
488+ logger : Mock . Of < ILogger < HttpsConnectionMiddleware > > ( ) ) ;
489+
490+ var options = sniOptionsSelector . GetOptions ( new MockConnectionContext ( ) , "www.example.org" ) ;
491+ var alpnList = options . ApplicationProtocols ;
492+
493+ Assert . NotNull ( alpnList ) ;
494+ var protocol = Assert . Single ( alpnList ) ;
495+ Assert . Equal ( SslApplicationProtocol . Http11 , protocol ) ;
496+ }
497+
498+ [ Fact ]
499+ public void FallsBackToFallbackHttpProtocols ( )
500+ {
501+ var sniDictionary = new Dictionary < string , SniConfig >
502+ {
503+ {
504+ "www.example.org" ,
505+ new SniConfig
506+ {
507+ Certificate = new CertificateConfig ( )
508+ }
509+ }
510+ } ;
511+
512+ var sniOptionsSelector = new SniOptionsSelector (
513+ "TestEndpointName" ,
514+ sniDictionary ,
515+ new MockCertificateConfigLoader ( ) ,
516+ new HttpsConnectionAdapterOptions ( ) ,
517+ fallbackHttpProtocols : HttpProtocols . Http1 ,
518+ logger : Mock . Of < ILogger < HttpsConnectionMiddleware > > ( ) ) ;
519+
520+ var mockConnectionContext = new MockConnectionContext ( ) ;
521+ sniOptionsSelector . GetOptions ( mockConnectionContext , "www.example.org" ) ;
522+
523+ var httpProtocolsFeature = mockConnectionContext . Features . Get < HttpProtocolsFeature > ( ) ;
524+ Assert . NotNull ( httpProtocolsFeature ) ;
525+ Assert . Equal ( HttpProtocols . Http1 , httpProtocolsFeature . HttpProtocols ) ;
526+ }
527+
528+ [ Fact ]
529+ public void PrefersSslProtocolsDefinedInSniConfig ( )
530+ {
531+ var sniDictionary = new Dictionary < string , SniConfig >
532+ {
533+ {
534+ "www.example.org" ,
535+ new SniConfig
536+ {
537+ SslProtocols = SslProtocols . Tls13 | SslProtocols . Tls11 ,
538+ Certificate = new CertificateConfig ( )
539+ }
540+ }
541+ } ;
542+
543+ var sniOptionsSelector = new SniOptionsSelector (
544+ "TestEndpointName" ,
545+ sniDictionary ,
546+ new MockCertificateConfigLoader ( ) ,
547+ new HttpsConnectionAdapterOptions
548+ {
549+ SslProtocols = SslProtocols . Tls13
550+ } ,
551+ fallbackHttpProtocols : HttpProtocols . Http1AndHttp2 ,
552+ logger : Mock . Of < ILogger < HttpsConnectionMiddleware > > ( ) ) ;
553+
554+ var options = sniOptionsSelector . GetOptions ( new MockConnectionContext ( ) , "www.example.org" ) ;
555+ Assert . Equal ( SslProtocols . Tls13 | SslProtocols . Tls11 , options . EnabledSslProtocols ) ;
556+ }
557+
558+ [ Fact ]
559+ public void FallsBackToFallbackSslProtocols ( )
560+ {
561+ var sniDictionary = new Dictionary < string , SniConfig >
562+ {
563+ {
564+ "www.example.org" ,
565+ new SniConfig
566+ {
567+ Certificate = new CertificateConfig ( )
568+ }
569+ }
570+ } ;
571+
572+ var sniOptionsSelector = new SniOptionsSelector (
573+ "TestEndpointName" ,
574+ sniDictionary ,
575+ new MockCertificateConfigLoader ( ) ,
576+ new HttpsConnectionAdapterOptions
577+ {
578+ SslProtocols = SslProtocols . Tls13
579+ } ,
580+ fallbackHttpProtocols : HttpProtocols . Http1AndHttp2 ,
581+ logger : Mock . Of < ILogger < HttpsConnectionMiddleware > > ( ) ) ;
582+
583+ var options = sniOptionsSelector . GetOptions ( new MockConnectionContext ( ) , "www.example.org" ) ;
584+ Assert . Equal ( SslProtocols . Tls13 , options . EnabledSslProtocols ) ;
585+ }
586+
587+
588+ [ Fact ]
589+ public void PrefersClientCertificateModeDefinedInSniConfig ( )
590+ {
591+ var sniDictionary = new Dictionary < string , SniConfig >
592+ {
593+ {
594+ "www.example.org" ,
595+ new SniConfig
596+ {
597+ ClientCertificateMode = ClientCertificateMode . RequireCertificate ,
598+ Certificate = new CertificateConfig ( )
599+ }
600+ }
601+ } ;
602+
603+ var sniOptionsSelector = new SniOptionsSelector (
604+ "TestEndpointName" ,
605+ sniDictionary ,
606+ new MockCertificateConfigLoader ( ) ,
607+ new HttpsConnectionAdapterOptions
608+ {
609+ ClientCertificateMode = ClientCertificateMode . AllowCertificate
610+ } ,
611+ fallbackHttpProtocols : HttpProtocols . Http1AndHttp2 ,
612+ logger : Mock . Of < ILogger < HttpsConnectionMiddleware > > ( ) ) ;
613+
614+ var options = sniOptionsSelector . GetOptions ( new MockConnectionContext ( ) , "www.example.org" ) ;
615+
616+ Assert . True ( options . ClientCertificateRequired ) ;
617+
618+ Assert . NotNull ( options . RemoteCertificateValidationCallback ) ;
619+ // The RemoteCertificateValidationCallback should first check if the certificate is null and return false since it's required.
620+ Assert . False ( options . RemoteCertificateValidationCallback ( sender : null , certificate : null , chain : null , SslPolicyErrors . None ) ) ;
621+ }
622+
623+ [ Fact ]
624+ public void FallsBackToFallbackClientCertificateMode ( )
625+ {
626+ var sniDictionary = new Dictionary < string , SniConfig >
627+ {
628+ {
629+ "www.example.org" ,
630+ new SniConfig
631+ {
632+ Certificate = new CertificateConfig ( )
633+ }
634+ }
635+ } ;
636+
637+ var sniOptionsSelector = new SniOptionsSelector (
638+ "TestEndpointName" ,
639+ sniDictionary ,
640+ new MockCertificateConfigLoader ( ) ,
641+ new HttpsConnectionAdapterOptions
642+ {
643+ ClientCertificateMode = ClientCertificateMode . AllowCertificate
644+ } ,
645+ fallbackHttpProtocols : HttpProtocols . Http1AndHttp2 ,
646+ logger : Mock . Of < ILogger < HttpsConnectionMiddleware > > ( ) ) ;
647+
648+ var options = sniOptionsSelector . GetOptions ( new MockConnectionContext ( ) , "www.example.org" ) ;
649+
650+ // Despite the confusing name, ClientCertificateRequired being true simply requests a certificate from the client, but doesn't require it.
651+ Assert . True ( options . ClientCertificateRequired ) ;
652+
653+ Assert . NotNull ( options . RemoteCertificateValidationCallback ) ;
654+ // The RemoteCertificateValidationCallback should see we're in the AllowCertificate mode and return true.
655+ Assert . True ( options . RemoteCertificateValidationCallback ( sender : null , certificate : null , chain : null , SslPolicyErrors . None ) ) ;
656+ }
657+
434658 [ Fact ]
435659 public void CloneSslOptionsClonesAllProperties ( )
436660 {
0 commit comments