1
1
// Copyright (c) .NET Foundation. All rights reserved.
2
2
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3
3
4
+ using System . Buffers ;
4
5
using System . Collections . Generic ;
5
6
using System . Linq ;
6
7
using System . Net ;
7
8
using System . Net . Http ;
8
9
using System . Net . Sockets ;
9
10
using System . Text ;
10
- using System . Threading ;
11
11
using System . Threading . Tasks ;
12
12
using Microsoft . AspNetCore . Http ;
13
13
using Microsoft . AspNetCore . Server . Kestrel . Core ;
14
14
using Microsoft . AspNetCore . Server . Kestrel . Core . Internal ;
15
- using Microsoft . AspNetCore . Server . Kestrel . Core . Internal . Infrastructure ;
16
15
using Microsoft . AspNetCore . Server . Kestrel . Transport . Libuv . Internal ;
17
16
using Microsoft . AspNetCore . Server . Kestrel . Transport . Libuv . Tests . TestHelpers ;
18
17
using Microsoft . AspNetCore . Testing ;
@@ -23,15 +22,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Tests
23
22
{
24
23
public class LibuvTransportTests
25
24
{
26
- public static TheoryData < ListenOptions > ConnectionAdapterData => new TheoryData < ListenOptions >
27
- {
28
- new ListenOptions ( new IPEndPoint ( IPAddress . Loopback , 0 ) ) ,
29
- new ListenOptions ( new IPEndPoint ( IPAddress . Loopback , 0 ) )
30
- {
31
- ConnectionAdapters = { new PassThroughConnectionAdapter ( ) }
32
- }
33
- } ;
34
-
35
25
public static IEnumerable < object [ ] > OneToTen => Enumerable . Range ( 1 , 10 ) . Select ( i => new object [ ] { i } ) ;
36
26
37
27
[ Fact ]
@@ -42,7 +32,7 @@ public async Task TransportCanBindAndStop()
42
32
43
33
// The transport can no longer start threads without binding to an endpoint.
44
34
await transport . BindAsync ( ) ;
45
- await transport . StopThreadsAsync ( ) ;
35
+ await transport . DisposeAsync ( ) ;
46
36
}
47
37
48
38
[ Fact ]
@@ -52,42 +42,92 @@ public async Task TransportCanBindUnbindAndStop()
52
42
var transport = new LibuvConnectionListener ( transportContext , new IPEndPoint ( IPAddress . Loopback , 0 ) ) ;
53
43
54
44
await transport . BindAsync ( ) ;
55
- await transport . UnbindAsync ( ) ;
56
- await transport . StopThreadsAsync ( ) ;
45
+ await transport . StopAsync ( ) ;
46
+ await transport . DisposeAsync ( ) ;
57
47
}
58
48
59
- [ Theory ]
60
- [ MemberData ( nameof ( ConnectionAdapterData ) ) ]
61
- public async Task ConnectionCanReadAndWrite ( ListenOptions listenOptions )
49
+ [ Fact ]
50
+ public async Task ConnectionCanReadAndWrite ( )
62
51
{
63
- var serviceContext = new TestServiceContext ( ) ;
64
- listenOptions . UseHttpServer ( listenOptions . ConnectionAdapters , serviceContext , new DummyApplication ( TestApp . EchoApp ) , HttpProtocols . Http1 ) ;
52
+ var endpoint = new IPEndPoint ( IPAddress . Loopback , 0 ) ;
65
53
66
54
var transportContext = new TestLibuvTransportContext ( ) ;
67
- var transport = new LibuvConnectionListener ( transportContext , listenOptions . EndPoint ) ;
55
+ var transport = new LibuvConnectionListener ( transportContext , endpoint ) ;
68
56
69
57
await transport . BindAsync ( ) ;
70
- listenOptions . EndPoint = transport . EndPoint ;
58
+ endpoint = ( IPEndPoint ) transport . EndPoint ;
71
59
72
- var dispatcher = new ConnectionDispatcher ( serviceContext , listenOptions . Build ( ) ) ;
73
- _ = dispatcher . StartAcceptingConnections ( transport ) ;
60
+ async Task EchoServerAsync ( )
61
+ {
62
+ await using var connection = await transport . AcceptAsync ( ) ;
74
63
75
- using ( var socket = TestConnection . CreateConnectedLoopbackSocket ( listenOptions . IPEndPoint . Port ) )
64
+ while ( true )
65
+ {
66
+ var result = await connection . Transport . Input . ReadAsync ( ) ;
67
+
68
+ if ( result . IsCompleted )
69
+ {
70
+ break ;
71
+ }
72
+ await connection . Transport . Output . WriteAsync ( result . Buffer . ToArray ( ) ) ;
73
+
74
+ connection . Transport . Input . AdvanceTo ( result . Buffer . End ) ;
75
+ }
76
+ }
77
+
78
+ var serverTask = EchoServerAsync ( ) ;
79
+
80
+ using ( var socket = TestConnection . CreateConnectedLoopbackSocket ( endpoint . Port ) )
76
81
{
77
- var data = "Hello World" ;
78
- socket . Send ( Encoding . ASCII . GetBytes ( $ "POST / HTTP/1.0\r \n Content-Length: 11\r \n \r \n { data } ") ) ;
82
+ var data = Encoding . ASCII . GetBytes ( "Hello World" ) ;
83
+ socket . Send ( data ) ;
84
+
79
85
var buffer = new byte [ data . Length ] ;
80
86
var read = 0 ;
81
87
while ( read < data . Length )
82
88
{
83
89
read += socket . Receive ( buffer , read , buffer . Length - read , SocketFlags . None ) ;
84
90
}
91
+
92
+ Assert . Equal ( data , buffer ) ;
85
93
}
86
94
87
-
88
- Assert . True ( await serviceContext . ConnectionManager . CloseAllConnectionsAsync ( new CancellationTokenSource ( TestConstants . DefaultTimeout ) . Token ) ) ;
89
- await transport . UnbindAsync ( ) ;
90
- await transport . StopThreadsAsync ( ) ;
95
+ await serverTask . DefaultTimeout ( ) ;
96
+
97
+ await transport . StopAsync ( ) ;
98
+ await transport . DisposeAsync ( ) ;
99
+ }
100
+
101
+ [ Fact ]
102
+ public async Task UnacceptedConnectionsAreAborted ( )
103
+ {
104
+ var endpoint = new IPEndPoint ( IPAddress . Loopback , 0 ) ;
105
+
106
+ var transportContext = new TestLibuvTransportContext ( ) ;
107
+ var transport = new LibuvConnectionListener ( transportContext , endpoint ) ;
108
+
109
+ await transport . BindAsync ( ) ;
110
+ endpoint = ( IPEndPoint ) transport . EndPoint ;
111
+
112
+ async Task ConnectAsync ( )
113
+ {
114
+ using ( var socket = TestConnection . CreateConnectedLoopbackSocket ( endpoint . Port ) )
115
+ {
116
+ var read = await socket . ReceiveAsync ( new byte [ 10 ] , SocketFlags . None ) ;
117
+ Assert . Equal ( 0 , read ) ;
118
+ }
119
+ }
120
+
121
+ var connectTask = ConnectAsync ( ) ;
122
+
123
+ await transport . StopAsync ( ) ;
124
+ await transport . DisposeAsync ( ) ;
125
+
126
+ // The connection was accepted because libuv eagerly accepts connections
127
+ // they sit in a queue in each listener, we want to make sure that resources
128
+ // are cleaned up if they are never accepted by the caller
129
+
130
+ await connectTask . DefaultTimeout ( ) ;
91
131
}
92
132
93
133
[ ConditionalTheory ]
@@ -132,7 +172,7 @@ public async Task OneToTenThreads(int threadCount)
132
172
}
133
173
}
134
174
135
- await transport . UnbindAsync ( ) . ConfigureAwait ( false ) ;
175
+ await transport . StopAsync ( ) . ConfigureAwait ( false ) ;
136
176
137
177
await acceptTask . ConfigureAwait ( false ) ;
138
178
@@ -141,7 +181,7 @@ public async Task OneToTenThreads(int threadCount)
141
181
await serviceContext . ConnectionManager . AbortAllConnectionsAsync ( ) . ConfigureAwait ( false ) ;
142
182
}
143
183
144
- await transport . StopThreadsAsync ( ) . ConfigureAwait ( false ) ;
184
+ await transport . DisposeAsync ( ) . ConfigureAwait ( false ) ;
145
185
}
146
186
}
147
187
}
0 commit comments