-
Notifications
You must be signed in to change notification settings - Fork 5.6k
Expand file tree
/
Copy pathSslStreamDisposeTest.cs
More file actions
171 lines (143 loc) · 7.44 KB
/
Copy pathSslStreamDisposeTest.cs
File metadata and controls
171 lines (143 loc) · 7.44 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.IO;
using System.Security.Cryptography.X509Certificates;
using System.Threading;
using System.Security.Authentication;
using System.Threading.Tasks;
using Xunit;
namespace System.Net.Security.Tests
{
using Configuration = System.Net.Test.Common.Configuration;
public class SslStreamDisposeTest
{
[Fact]
public async Task DisposeAsync_NotConnected_ClosesStream()
{
bool disposed = false;
var stream = new SslStream(new DelegateStream(disposeFunc: _ => disposed = true, canReadFunc: () => true, canWriteFunc: () => true), false, delegate { return true; });
Assert.False(disposed);
await stream.DisposeAsync();
Assert.True(disposed);
}
[Fact]
public async Task DisposeAsync_Connected_ClosesStream()
{
(Stream stream1, Stream stream2) = TestHelper.GetConnectedStreams();
var trackingStream1 = new CallTrackingStream(stream1);
var trackingStream2 = new CallTrackingStream(stream2);
var clientStream = new SslStream(trackingStream1, false, delegate { return true; });
var serverStream = new SslStream(trackingStream2, false, delegate { return true; });
using (X509Certificate2 certificate = Configuration.Certificates.GetServerCertificate())
{
await TestConfiguration.WhenAllOrAnyFailedWithTimeout(
clientStream.AuthenticateAsClientAsync(certificate.GetNameInfo(X509NameType.SimpleName, false)),
serverStream.AuthenticateAsServerAsync(certificate));
}
Assert.Equal(0, trackingStream1.TimesCalled(nameof(Stream.DisposeAsync)));
await clientStream.DisposeAsync();
Assert.NotEqual(0, trackingStream1.TimesCalled(nameof(Stream.DisposeAsync)));
Assert.Equal(0, trackingStream2.TimesCalled(nameof(Stream.DisposeAsync)));
await serverStream.DisposeAsync();
Assert.NotEqual(0, trackingStream2.TimesCalled(nameof(Stream.DisposeAsync)));
}
[Theory]
[InlineData(true)]
[InlineData(false)]
public async Task Dispose_PendingReadAsync_ThrowsODE(bool bufferedRead)
{
using CancellationTokenSource cts = new CancellationTokenSource();
cts.CancelAfter(TestConfiguration.PassingTestTimeout);
(SslStream client, SslStream server) = TestHelper.GetConnectedSslStreams(leaveInnerStreamOpen: true);
using (client)
using (server)
using (X509Certificate2 serverCertificate = Configuration.Certificates.GetServerCertificate())
using (X509Certificate2 clientCertificate = Configuration.Certificates.GetClientCertificate())
{
SslClientAuthenticationOptions clientOptions = new SslClientAuthenticationOptions()
{
TargetHost = Guid.NewGuid().ToString("N"),
};
clientOptions.RemoteCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) => true;
SslServerAuthenticationOptions serverOptions = new SslServerAuthenticationOptions()
{
ServerCertificate = serverCertificate,
};
await TestConfiguration.WhenAllOrAnyFailedWithTimeout(
client.AuthenticateAsClientAsync(clientOptions, default),
server.AuthenticateAsServerAsync(serverOptions, default));
await TestHelper.PingPong(client, server, cts.Token);
await server.WriteAsync("PINGPONG"u8.ToArray(), cts.Token);
var readBuffer = new byte[1024];
Task<int>? task = null;
if (bufferedRead)
{
// This will read everything into internal buffer. Following ReadAsync will not need IO.
task = client.ReadAsync(readBuffer, 0, 4, cts.Token);
int readLength = await task.ConfigureAwait(false);
client.Dispose();
Assert.Equal(4, readLength);
}
else
{
client.Dispose();
}
await Assert.ThrowsAnyAsync<ObjectDisposedException>(() => client.ReadAsync(readBuffer, cts.Token).AsTask());
}
}
[Fact]
[OuterLoop("Computationally expensive")]
public async Task Dispose_ParallelWithHandshake_ThrowsODE()
{
using CancellationTokenSource cts = new CancellationTokenSource();
cts.CancelAfter(TestConfiguration.PassingTestTimeout);
await Parallel.ForEachAsync(System.Linq.Enumerable.Range(0, 10000), cts.Token, async (i, token) =>
{
(Stream clientStream, Stream serverStream) = TestHelper.GetConnectedStreams();
using SslStream client = new SslStream(clientStream);
using SslStream server = new SslStream(serverStream);
using X509Certificate2 serverCertificate = Configuration.Certificates.GetServerCertificate();
using X509Certificate2 clientCertificate = Configuration.Certificates.GetClientCertificate();
SslClientAuthenticationOptions clientOptions = new SslClientAuthenticationOptions()
{
TargetHost = Guid.NewGuid().ToString("N"),
RemoteCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) => true,
};
SslServerAuthenticationOptions serverOptions = new SslServerAuthenticationOptions()
{
ServerCertificate = serverCertificate,
};
var clientTask = Task.Run(() => client.AuthenticateAsClientAsync(clientOptions, cts.Token));
var serverTask = Task.Run(() => server.AuthenticateAsServerAsync(serverOptions, cts.Token));
// Dispose the instances while the handshake is in progress.
client.Dispose();
server.Dispose();
await ValidateExceptionAsync(clientTask);
await ValidateExceptionAsync(serverTask);
});
static async Task ValidateExceptionAsync(Task task)
{
try
{
await task;
}
catch (InvalidOperationException ex) when (ex.StackTrace is null ||
ex.StackTrace.Contains("System.IO.ConnectedStreams") ||
ex.StackTrace.Contains("System.IO.StreamBuffer.TryWriteToBuffer") ||
ex.StackTrace.Contains("System.IO.StreamBuffer.WriteAsync"))
{
// Writing to a disposed ConnectedStream (test only, does not happen with NetworkStream)
return;
}
catch (Exception ex) when (ex
is ObjectDisposedException // disposed locally
or IOException // disposed remotely (received unexpected EOF)
or AuthenticationException) // disposed wrapped in AuthenticationException or error from platform library
{
// expected
return;
}
}
}
}
}