Avoid zero-byte send in WebSockets (#3326)
This commit is contained in:
parent
dde73d0b8a
commit
c852bdcc33
|
|
@ -39,22 +39,28 @@ namespace System.Net.WebSockets
|
||||||
private static async ValueTask SendMultiSegmentAsync(WebSocket webSocket, ReadOnlySequence<byte> buffer, WebSocketMessageType webSocketMessageType, CancellationToken cancellationToken = default)
|
private static async ValueTask SendMultiSegmentAsync(WebSocket webSocket, ReadOnlySequence<byte> buffer, WebSocketMessageType webSocketMessageType, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
var position = buffer.Start;
|
var position = buffer.Start;
|
||||||
|
// Get a segment before the loop so we can be one segment behind while writing
|
||||||
|
// This allows us to do a non-zero byte write for the endOfMessage = true send
|
||||||
|
buffer.TryGet(ref position, out var prevSegment);
|
||||||
while (buffer.TryGet(ref position, out var segment))
|
while (buffer.TryGet(ref position, out var segment))
|
||||||
{
|
{
|
||||||
#if NETCOREAPP3_0
|
#if NETCOREAPP3_0
|
||||||
await webSocket.SendAsync(segment, webSocketMessageType, endOfMessage: false, cancellationToken);
|
await webSocket.SendAsync(prevSegment, webSocketMessageType, endOfMessage: false, cancellationToken);
|
||||||
#else
|
#else
|
||||||
var isArray = MemoryMarshal.TryGetArray(segment, out var arraySegment);
|
var isArray = MemoryMarshal.TryGetArray(prevSegment, out var arraySegment);
|
||||||
Debug.Assert(isArray);
|
Debug.Assert(isArray);
|
||||||
await webSocket.SendAsync(arraySegment, webSocketMessageType, endOfMessage: false, cancellationToken);
|
await webSocket.SendAsync(arraySegment, webSocketMessageType, endOfMessage: false, cancellationToken);
|
||||||
#endif
|
#endif
|
||||||
|
prevSegment = segment;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Empty end of message frame
|
// End of message frame
|
||||||
#if NETCOREAPP3_0
|
#if NETCOREAPP3_0
|
||||||
await webSocket.SendAsync(Memory<byte>.Empty, webSocketMessageType, endOfMessage: true, cancellationToken);
|
await webSocket.SendAsync(prevSegment, webSocketMessageType, endOfMessage: true, cancellationToken);
|
||||||
#else
|
#else
|
||||||
await webSocket.SendAsync(new ArraySegment<byte>(Array.Empty<byte>()), webSocketMessageType, endOfMessage: true, cancellationToken);
|
var isArrayEnd = MemoryMarshal.TryGetArray(prevSegment, out var arraySegmentEnd);
|
||||||
|
Debug.Assert(isArrayEnd);
|
||||||
|
await webSocket.SendAsync(arraySegmentEnd, webSocketMessageType, endOfMessage: true, cancellationToken);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -396,5 +396,36 @@ namespace Microsoft.AspNetCore.Http.Connections.Tests
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task MultiSegmentSendWillNotSendEmptyEndOfMessageFrame()
|
||||||
|
{
|
||||||
|
using (var feature = new TestWebSocketConnectionFeature())
|
||||||
|
{
|
||||||
|
var serverSocket = await feature.AcceptAsync();
|
||||||
|
var sequence = ReadOnlySequenceFactory.CreateSegments(new byte[] { 1 }, new byte[] { 15 });
|
||||||
|
Assert.False(sequence.IsSingleSegment);
|
||||||
|
|
||||||
|
await serverSocket.SendAsync(sequence, WebSocketMessageType.Text);
|
||||||
|
|
||||||
|
// Run the client socket
|
||||||
|
var client = feature.Client.ExecuteAndCaptureFramesAsync();
|
||||||
|
|
||||||
|
await serverSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "", default);
|
||||||
|
|
||||||
|
var messages = await client.OrTimeout();
|
||||||
|
Assert.Equal(2, messages.Received.Count);
|
||||||
|
|
||||||
|
// First message: 1 byte, endOfMessage false
|
||||||
|
Assert.Single(messages.Received[0].Buffer);
|
||||||
|
Assert.Equal(1, messages.Received[0].Buffer[0]);
|
||||||
|
Assert.False(messages.Received[0].EndOfMessage);
|
||||||
|
|
||||||
|
// Second message: 1 byte, endOfMessage true
|
||||||
|
Assert.Single(messages.Received[1].Buffer);
|
||||||
|
Assert.Equal(15, messages.Received[1].Buffer[0]);
|
||||||
|
Assert.True(messages.Received[1].EndOfMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue