@@ -102,5 +102,57 @@ await TestConfiguration.WhenAllOrAnyFailedWithTimeout(
102
102
await Assert . ThrowsAnyAsync < ObjectDisposedException > ( ( ) => client . ReadAsync ( readBuffer , cts . Token ) . AsTask ( ) ) ;
103
103
}
104
104
}
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
+ }
105
157
}
106
158
}
0 commit comments