-
Notifications
You must be signed in to change notification settings - Fork 4.6k
/
QuicStreamConnectedStreamConformanceTests.cs
157 lines (142 loc) · 6.35 KB
/
QuicStreamConnectedStreamConformanceTests.cs
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
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Collections.Generic;
using System.IO;
using System.IO.Tests;
using System.Net.Sockets;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
using System.Threading;
using System.Threading.Tasks;
using Xunit;
using Xunit.Abstractions;
namespace System.Net.Quic.Tests
{
[Collection(nameof(DisableParallelization))]
[ConditionalClass(typeof(QuicTestBase), nameof(QuicTestBase.IsSupported))]
public sealed class QuicStreamConformanceTests : ConnectedStreamConformanceTests
{
protected override bool UsableAfterCanceledReads => false;
protected override bool BlocksOnZeroByteReads => true;
protected override bool CanTimeout => true;
public readonly X509Certificate2 ServerCertificate = System.Net.Test.Common.Configuration.Certificates.GetServerCertificate();
public ITestOutputHelper _output;
protected override void Dispose(bool disposing)
{
if (disposing)
{
ServerCertificate.Dispose();
}
base.Dispose(disposing);
}
public bool RemoteCertificateValidationCallback(object sender, X509Certificate? certificate, X509Chain? chain, SslPolicyErrors sslPolicyErrors)
{
Assert.Equal(ServerCertificate.GetCertHash(), certificate?.GetCertHash());
return true;
}
public SslServerAuthenticationOptions GetSslServerAuthenticationOptions()
{
return new SslServerAuthenticationOptions()
{
ApplicationProtocols = new List<SslApplicationProtocol>() { new SslApplicationProtocol("quictest") },
ServerCertificate = ServerCertificate
};
}
public SslClientAuthenticationOptions GetSslClientAuthenticationOptions()
{
return new SslClientAuthenticationOptions()
{
ApplicationProtocols = new List<SslApplicationProtocol>() { new SslApplicationProtocol("quictest") },
RemoteCertificateValidationCallback = RemoteCertificateValidationCallback
};
}
protected override async Task<StreamPair> CreateConnectedStreamsAsync()
{
var listener = await QuicListener.ListenAsync(new QuicListenerOptions()
{
ListenEndPoint = new IPEndPoint(IPAddress.Loopback, 0),
ApplicationProtocols = new List<SslApplicationProtocol>() { new SslApplicationProtocol("quictest") },
ConnectionOptionsCallback = (_, _, _) => ValueTask.FromResult(new QuicServerConnectionOptions()
{
DefaultStreamErrorCode = QuicTestBase.DefaultStreamErrorCodeServer,
DefaultCloseErrorCode = QuicTestBase.DefaultCloseErrorCodeServer,
ServerAuthenticationOptions = GetSslServerAuthenticationOptions()
})
});
byte[] buffer = new byte[1] { 42 };
QuicConnection connection1 = null, connection2 = null;
QuicStream stream1 = null, stream2 = null;
try
{
await WhenAllOrAnyFailed(
Task.Run(async () =>
{
connection1 = await listener.AcceptConnectionAsync();
stream1 = await connection1.AcceptInboundStreamAsync();
Assert.Equal(1, await stream1.ReadAsync(buffer));
}),
Task.Run(async () =>
{
try
{
connection2 = await QuicConnection.ConnectAsync(new QuicClientConnectionOptions()
{
DefaultStreamErrorCode = QuicTestBase.DefaultStreamErrorCodeClient,
DefaultCloseErrorCode = QuicTestBase.DefaultCloseErrorCodeClient,
RemoteEndPoint = listener.LocalEndPoint,
ClientAuthenticationOptions = GetSslClientAuthenticationOptions()
});
stream2 = await connection2.OpenOutboundStreamAsync(QuicStreamType.Bidirectional);
// OpenBidirectionalStream only allocates ID. We will force stream opening
// by Writing there and receiving data on the other side.
await stream2.WriteAsync(buffer);
}
catch (Exception ex)
{
_output?.WriteLine($"Failed to connect: {ex.Message}");
throw;
}
}));
// No need to keep the listener once we have connected connection and streams
await listener.DisposeAsync();
var result = new StreamPairWithOtherDisposables(stream1, stream2);
result.Disposables.Add(connection1);
result.Disposables.Add(connection2);
return result;
}
catch
{
if (stream1 is not null)
{
await stream1.DisposeAsync();
}
if (stream2 is not null)
{
await stream2.DisposeAsync();
}
if (connection1 is not null)
{
await connection1.DisposeAsync();
}
if (connection2 is not null)
{
await connection2.DisposeAsync();
}
throw;
}
}
private sealed class StreamPairWithOtherDisposables : StreamPair
{
public readonly List<IAsyncDisposable> Disposables = new List<IAsyncDisposable>();
public StreamPairWithOtherDisposables(Stream stream1, Stream stream2) : base(stream1, stream2) { }
public override void Dispose()
{
base.Dispose();
foreach (IAsyncDisposable disposable in Disposables)
{
disposable.DisposeAsync().GetAwaiter().GetResult();
}
}
}
}
}