Skip to content

Commit bb2d9ac

Browse files
[Named pipes] Disconnect + delayed client read test cases (dotnet#46166)
Co-authored-by: James Newton-King <[email protected]>
1 parent c48a0eb commit bb2d9ac

File tree

2 files changed

+72
-4
lines changed

2 files changed

+72
-4
lines changed

src/Servers/Kestrel/Transport.NamedPipes/src/Internal/NamedPipeConnection.cs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,10 @@ internal sealed class NamedPipeConnection : TransportConnection, IConnectionName
2424
private bool _connectionDisposed;
2525
private Exception? _shutdownReason;
2626
private readonly object _shutdownLock = new object();
27-
28-
private Task _receivingTask = Task.CompletedTask;
29-
private Task _sendingTask = Task.CompletedTask;
27+
28+
// Internal for testing.
29+
internal Task _receivingTask = Task.CompletedTask;
30+
internal Task _sendingTask = Task.CompletedTask;
3031

3132
public NamedPipeConnection(
3233
NamedPipeServerStream stream,

src/Servers/Kestrel/Transport.NamedPipes/test/NamedPipeConnectionTests.cs

Lines changed: 68 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using System.Text;
55
using Microsoft.AspNetCore.Connections;
66
using Microsoft.AspNetCore.Internal;
7+
using Microsoft.AspNetCore.Server.Kestrel.Transport.NamedPipes.Internal;
78
using Microsoft.AspNetCore.Testing;
89

910
namespace Microsoft.AspNetCore.Server.Kestrel.Transport.NamedPipes.Tests;
@@ -39,7 +40,7 @@ public async Task InputReadAsync_ServerAborted_ThrowError()
3940
var clientStream = NamedPipeTestHelpers.CreateClientStream(connectionListener.EndPoint);
4041
await clientStream.ConnectAsync().DefaultTimeout();
4142
await clientStream.WriteAsync(TestData).DefaultTimeout();
42-
43+
4344
var serverConnection = await connectionListener.AcceptAsync().DefaultTimeout();
4445
var readResult = await serverConnection.Transport.Input.ReadAtLeastAsync(TestData.Length).DefaultTimeout();
4546
serverConnection.Transport.Input.AdvanceTo(readResult.Buffer.End);
@@ -103,4 +104,70 @@ public async Task OutputWriteAsync_ServerAborted_ThrowError()
103104
// Complete writing.
104105
await serverConnection.Transport.Output.CompleteAsync();
105106
}
107+
108+
[ConditionalFact]
109+
[OSSkipCondition(OperatingSystems.Linux | OperatingSystems.MacOSX, SkipReason = "Non-OS implementations use UDS with different transport behavior.")]
110+
public async Task CompleteTransport_BeforeClientHasReadLastBytes_DontLoseData()
111+
{
112+
var options = new NamedPipeTransportOptions();
113+
await using var connectionListener = await NamedPipeTestHelpers.CreateConnectionListenerFactory(LoggerFactory, options: options);
114+
115+
var clientStream = NamedPipeTestHelpers.CreateClientStream(connectionListener.EndPoint);
116+
var connectTask = clientStream.ConnectAsync();
117+
118+
var connection = (NamedPipeConnection)await connectionListener.AcceptAsync();
119+
await connection.Transport.Output.WriteAsync(new byte[] { 41 });
120+
byte[] readBuffer = new byte[1];
121+
var readAmount = await clientStream.ReadAsync(readBuffer);
122+
Assert.Equal(1, readAmount);
123+
Assert.Equal(41, readBuffer[0]);
124+
await connection.Transport.Output.WriteAsync(new byte[] { 42 });
125+
126+
connection.Transport.Output.Complete();
127+
128+
// Wait for a short amount of time after completing transport before asserting the server is waiting for the client to finish reading.
129+
// Actual stream disconnection should happen after the last client read happens.
130+
await Task.Delay(100);
131+
Assert.False(connection._sendingTask.IsCompleted, "Sending isn't complete until the client reads data.");
132+
133+
readAmount = await clientStream.ReadAsync(readBuffer);
134+
Assert.Equal(1, readAmount);
135+
Assert.Equal(42, readBuffer[0]);
136+
137+
await connection._sendingTask.DefaultTimeout();
138+
}
139+
140+
[ConditionalFact]
141+
[OSSkipCondition(OperatingSystems.Linux | OperatingSystems.MacOSX, SkipReason = "Non-OS implementations use UDS with different transport behavior.")]
142+
public async Task DisposeAsync_BeforeClientHasReadLastBytes_DontLoseData()
143+
{
144+
var options = new NamedPipeTransportOptions();
145+
await using var connectionListener = await NamedPipeTestHelpers.CreateConnectionListenerFactory(LoggerFactory, options: options);
146+
147+
var clientStream = NamedPipeTestHelpers.CreateClientStream(connectionListener.EndPoint);
148+
var connectTask = clientStream.ConnectAsync();
149+
150+
var connection = (NamedPipeConnection)await connectionListener.AcceptAsync();
151+
await connection.Transport.Output.WriteAsync(new byte[] { 41 });
152+
byte[] readBuffer = new byte[1];
153+
var readAmount = await clientStream.ReadAsync(readBuffer);
154+
Assert.Equal(1, readAmount);
155+
Assert.Equal(41, readBuffer[0]);
156+
await connection.Transport.Output.WriteAsync(new byte[] { 42 });
157+
158+
var disposeTask = connection.DisposeAsync();
159+
160+
// Wait for a short amount of time after completing transport before asserting the server is waiting for the client to finish reading.
161+
// Actual stream disconnection should happen after the last client read happens.
162+
await Task.Delay(100);
163+
Assert.False(connection._sendingTask.IsCompleted, "Sending isn't complete until the client reads data.");
164+
Assert.False(disposeTask.IsCompleted, "Sending isn't complete until the client reads data.");
165+
166+
readAmount = await clientStream.ReadAsync(readBuffer);
167+
Assert.Equal(1, readAmount);
168+
Assert.Equal(42, readBuffer[0]);
169+
170+
await connection._sendingTask.DefaultTimeout();
171+
await disposeTask.DefaultTimeout();
172+
}
106173
}

0 commit comments

Comments
 (0)