2
2
// The .NET Foundation licenses this file to you under the MIT license.
3
3
4
4
using System ;
5
+ using System . Net ;
5
6
using System . Net . Quic ;
7
+ using System . Net . Security ;
8
+ using System . Security . Cryptography . X509Certificates ;
9
+ using System . Text ;
6
10
using System . Threading . Tasks ;
11
+ using Microsoft . AspNetCore . Http . Features ;
7
12
using Microsoft . AspNetCore . Server . Kestrel . FunctionalTests ;
8
13
using Microsoft . AspNetCore . Testing ;
9
14
using Xunit ;
@@ -13,6 +18,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Quic.Tests
13
18
[ QuarantinedTest ( "https://github.com/dotnet/aspnetcore/issues/35070" ) ]
14
19
public class QuicConnectionListenerTests : TestApplicationErrorLoggerLoggedTest
15
20
{
21
+ private static readonly byte [ ] TestData = Encoding . UTF8 . GetBytes ( "Hello world" ) ;
22
+
16
23
[ ConditionalFact ]
17
24
[ MsQuicSupported ]
18
25
public async Task AcceptAsync_AfterUnbind_Error ( )
@@ -51,5 +58,66 @@ public async Task AcceptAsync_ClientCreatesConnection_ServerAccepts()
51
58
// ConnectionClosed isn't triggered because the server initiated close.
52
59
Assert . False ( serverConnection . ConnectionClosed . IsCancellationRequested ) ;
53
60
}
61
+
62
+ [ ConditionalFact ]
63
+ [ MsQuicSupported ]
64
+ [ OSSkipCondition ( OperatingSystems . Linux | OperatingSystems . MacOSX ) ]
65
+ public async Task ClientCertificate_Required_Sent_Populated ( )
66
+ {
67
+ // Arrange
68
+ await using var connectionListener = await QuicTestHelpers . CreateConnectionListenerFactory ( LoggerFactory , clientCertificateRequired : true ) ;
69
+
70
+ var options = QuicTestHelpers . CreateClientConnectionOptions ( connectionListener . EndPoint ) ;
71
+ var testCert = TestResources . GetTestCertificate ( ) ;
72
+ options . ClientAuthenticationOptions . ClientCertificates = new X509CertificateCollection { testCert } ;
73
+
74
+ // Act
75
+ using var quicConnection = new QuicConnection ( options ) ;
76
+ await quicConnection . ConnectAsync ( ) . DefaultTimeout ( ) ;
77
+
78
+ var serverConnection = await connectionListener . AcceptAndAddFeatureAsync ( ) . DefaultTimeout ( ) ;
79
+ // Server waits for stream from client
80
+ var serverStreamTask = serverConnection . AcceptAsync ( ) . DefaultTimeout ( ) ;
81
+
82
+ // Client creates stream
83
+ using var clientStream = quicConnection . OpenBidirectionalStream ( ) ;
84
+ await clientStream . WriteAsync ( TestData ) . DefaultTimeout ( ) ;
85
+
86
+ // Server finishes accepting
87
+ var serverStream = await serverStreamTask . DefaultTimeout ( ) ;
88
+
89
+ // Assert
90
+ AssertTlsConnectionFeature ( serverConnection . Features , testCert ) ;
91
+ AssertTlsConnectionFeature ( serverStream . Features , testCert ) ;
92
+
93
+ static void AssertTlsConnectionFeature ( IFeatureCollection features , X509Certificate2 testCert )
94
+ {
95
+ var tlsFeature = features . Get < ITlsConnectionFeature > ( ) ;
96
+ Assert . NotNull ( tlsFeature ) ;
97
+ Assert . NotNull ( tlsFeature . ClientCertificate ) ;
98
+ Assert . Equal ( testCert , tlsFeature . ClientCertificate ) ;
99
+ }
100
+ }
101
+
102
+ [ ConditionalFact ]
103
+ [ MsQuicSupported ]
104
+ // https://github.com/dotnet/runtime/issues/57308, RemoteCertificateValidationCallback should allow us to accept a null cert,
105
+ // but it doesn't right now.
106
+ [ OSSkipCondition ( OperatingSystems . Linux | OperatingSystems . MacOSX ) ]
107
+ public async Task ClientCertificate_Required_NotSent_ConnectionAborted ( )
108
+ {
109
+ await using var connectionListener = await QuicTestHelpers . CreateConnectionListenerFactory ( LoggerFactory , clientCertificateRequired : true ) ;
110
+
111
+ var options = QuicTestHelpers . CreateClientConnectionOptions ( connectionListener . EndPoint ) ;
112
+ using var clientConnection = new QuicConnection ( options ) ;
113
+
114
+ var qex = await Assert . ThrowsAsync < QuicException > ( async ( ) => await clientConnection . ConnectAsync ( ) . DefaultTimeout ( ) ) ;
115
+ Assert . Equal ( "Connection has been shutdown by transport. Error Code: 0x80410100" , qex . Message ) ;
116
+
117
+ // https://github.com/dotnet/runtime/issues/57246 The accept still completes even though the connection was rejected, but it's already failed.
118
+ var serverContext = await connectionListener . AcceptAndAddFeatureAsync ( ) . DefaultTimeout ( ) ;
119
+ qex = await Assert . ThrowsAsync < QuicException > ( ( ) => serverContext . ConnectAsync ( ) . DefaultTimeout ( ) ) ;
120
+ Assert . Equal ( "Failed to open stream to peer. Error Code: INVALID_STATE" , qex . Message ) ;
121
+ }
54
122
}
55
123
}
0 commit comments