@@ -102,5 +102,57 @@ await TestConfiguration.WhenAllOrAnyFailedWithTimeout(
102102 await Assert . ThrowsAnyAsync < ObjectDisposedException > ( ( ) => client . ReadAsync ( readBuffer , cts . Token ) . AsTask ( ) ) ;
103103 }
104104 }
105+
106+ [ Fact ]
107+ [ OuterLoop ( "Computationally expensive" ) ]
108+ public async Task Dispose_ParallelWithHandshake_ThrowsODE ( )
109+ {
110+ using CancellationTokenSource cts = new CancellationTokenSource ( ) ;
111+ cts . CancelAfter ( TestConfiguration . PassingTestTimeout ) ;
112+
113+ await Parallel . ForEachAsync ( System . Linq . Enumerable . Range ( 0 , 10000 ) , cts . Token , async ( i , token ) =>
114+ {
115+ ( SslStream client , SslStream server ) = TestHelper . GetConnectedSslStreams ( ) ;
116+ using ( client )
117+ using ( server )
118+ using ( X509Certificate2 serverCertificate = Configuration . Certificates . GetServerCertificate ( ) )
119+ using ( X509Certificate2 clientCertificate = Configuration . Certificates . GetClientCertificate ( ) )
120+ {
121+ SslClientAuthenticationOptions clientOptions = new SslClientAuthenticationOptions ( )
122+ {
123+ TargetHost = Guid . NewGuid ( ) . ToString ( "N" ) ,
124+ RemoteCertificateValidationCallback = ( sender , certificate , chain , sslPolicyErrors ) => true ,
125+ } ;
126+
127+ SslServerAuthenticationOptions serverOptions = new SslServerAuthenticationOptions ( )
128+ {
129+ ServerCertificate = serverCertificate ,
130+ } ;
131+
132+ var clientTask = Task . Run ( ( ) => client . AuthenticateAsClientAsync ( clientOptions , cts . Token ) ) ;
133+ var serverTask = Task . Run ( ( ) => server . AuthenticateAsServerAsync ( serverOptions , cts . Token ) ) ;
134+
135+ // Dispose the instances while the handshake is in progress.
136+ client . Dispose ( ) ;
137+ server . Dispose ( ) ;
138+
139+ await ValidateExceptionAsync ( clientTask ) ;
140+ await ValidateExceptionAsync ( serverTask ) ;
141+ }
142+ } ) ;
143+
144+ static async Task ValidateExceptionAsync ( Task task )
145+ {
146+ try
147+ {
148+ await task ;
149+ }
150+ // either we disposed the stream, or the other side does and we get unexpected EOF
151+ catch ( Exception ex ) when ( ex is ObjectDisposedException or IOException )
152+ {
153+ return ;
154+ }
155+ }
156+ }
105157 }
106158}
0 commit comments