@@ -517,4 +517,77 @@ mod tests {
517517 ) ;
518518 assert ! ( resolver. get( ) . has_cert_for_sni( "app.example.com" ) ) ;
519519 }
520+
521+ #[ test]
522+ fn exact_certificate_precedes_parent_wildcard ( ) {
523+ let wildcard = make_test_cert_data ( ) ;
524+ let exact = make_test_cert_data ( ) ;
525+ let mut builder = CertStoreBuilder :: new ( ) ;
526+ builder
527+ . add_cert ( "example.com" , & wildcard)
528+ . expect ( "failed to add wildcard certificate" ) ;
529+ builder
530+ . add_exact_cert ( "api.example.com" , & exact)
531+ . expect ( "failed to add exact certificate" ) ;
532+ let store = builder. build ( ) ;
533+
534+ let exact_selected = store
535+ . resolve_cert ( "api.example.com" )
536+ . expect ( "exact certificate not selected" ) ;
537+ let wildcard_selected = store
538+ . resolve_cert ( "www.example.com" )
539+ . expect ( "wildcard certificate not selected" ) ;
540+ assert ! ( Arc :: ptr_eq(
541+ & exact_selected,
542+ store
543+ . exact_certs
544+ . get( "api.example.com" )
545+ . expect( "exact certificate missing" )
546+ ) ) ;
547+ assert ! ( Arc :: ptr_eq(
548+ & wildcard_selected,
549+ store
550+ . wildcard_certs
551+ . get( "example.com" )
552+ . expect( "wildcard certificate missing" )
553+ ) ) ;
554+ assert ! ( store. resolve_cert( "deep.www.example.com" ) . is_none( ) ) ;
555+ }
556+
557+ #[ test]
558+ fn concurrent_reads_never_observe_empty_during_reload ( ) {
559+ use std:: sync:: atomic:: { AtomicBool , AtomicUsize , Ordering } ;
560+
561+ let resolver = Arc :: new ( CertResolver :: new ( ) ) ;
562+ resolver
563+ . update_cert ( "example.com" , & make_test_cert_data ( ) )
564+ . expect ( "failed to install initial certificate" ) ;
565+ let finished = Arc :: new ( AtomicBool :: new ( false ) ) ;
566+ let misses = Arc :: new ( AtomicUsize :: new ( 0 ) ) ;
567+ let readers = ( 0 ..4 )
568+ . map ( |_| {
569+ let resolver = resolver. clone ( ) ;
570+ let finished = finished. clone ( ) ;
571+ let misses = misses. clone ( ) ;
572+ std:: thread:: spawn ( move || {
573+ while !finished. load ( Ordering :: Acquire ) {
574+ if resolver. get ( ) . resolve_cert ( "app.example.com" ) . is_none ( ) {
575+ misses. fetch_add ( 1 , Ordering :: Relaxed ) ;
576+ }
577+ }
578+ } )
579+ } )
580+ . collect :: < Vec < _ > > ( ) ;
581+
582+ for _ in 0 ..20 {
583+ resolver
584+ . update_cert ( "example.com" , & make_test_cert_data ( ) )
585+ . expect ( "hot reload failed" ) ;
586+ }
587+ finished. store ( true , Ordering :: Release ) ;
588+ for reader in readers {
589+ reader. join ( ) . expect ( "reader thread failed" ) ;
590+ }
591+ assert_eq ! ( misses. load( Ordering :: Relaxed ) , 0 ) ;
592+ }
520593}
0 commit comments