// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Globalization; using System.IO.Pipelines; using System.Linq; using System.Text; using System.Threading.Tasks; using Xunit; namespace Microsoft.Extensions.WebSockets.Internal.Tests { public partial class WebSocketConnectionTests { public class PingPongBehavior { [Fact] public async Task AutomaticPingTransmission() { var startTime = DateTime.UtcNow; // Arrange using (var pair = WebSocketPair.Create( serverOptions: new WebSocketOptions().WithAllFramesPassedThrough().WithPingInterval(TimeSpan.FromMilliseconds(100)), clientOptions: new WebSocketOptions().WithAllFramesPassedThrough())) { var client = pair.ClientSocket.ExecuteAndCaptureFramesAsync(); var server = pair.ServerSocket.ExecuteAndCaptureFramesAsync(); // Act // Wait for pings to be sent await Task.Delay(500); await pair.ServerSocket.CloseAsync(WebSocketCloseStatus.NormalClosure).OrTimeout(); var clientSummary = await client.OrTimeout(); await pair.ClientSocket.CloseAsync(WebSocketCloseStatus.NormalClosure).OrTimeout(); var serverSummary = await server.OrTimeout(); // Assert Assert.NotEqual(0, clientSummary.Received.Count); Assert.True(clientSummary.Received.All(f => f.EndOfMessage)); Assert.True(clientSummary.Received.All(f => f.Opcode == WebSocketOpcode.Ping)); Assert.True(clientSummary.Received.All(f => { var str = Encoding.UTF8.GetString(f.Payload.ToArray()); // We can't verify the exact timestamp, but we can verify that it is a timestamp created after we started. DateTime dt; if (DateTime.TryParseExact(str, "O", CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal, out dt)) { return dt >= startTime; } return false; })); } } [Fact] public async Task AutomaticPingResponse() { // Arrange using (var pair = WebSocketPair.Create( serverOptions: new WebSocketOptions().WithAllFramesPassedThrough(), clientOptions: new WebSocketOptions().WithAllFramesPassedThrough())) { var payload = Encoding.UTF8.GetBytes("ping payload"); var pongTcs = new TaskCompletionSource(); var client = pair.ClientSocket.ExecuteAsync(f => { if (f.Opcode == WebSocketOpcode.Pong) { pongTcs.TrySetResult(f.Copy()); } else { Assert.False(true, "Received non-pong frame from server!"); } }); var server = pair.ServerSocket.ExecuteAndCaptureFramesAsync(); // Act await pair.ClientSocket.SendAsync(new WebSocketFrame( endOfMessage: true, opcode: WebSocketOpcode.Ping, payload: ReadableBuffer.Create(payload))); var pongFrame = await pongTcs.Task.OrTimeout(); await pair.ClientSocket.CloseAsync(WebSocketCloseStatus.NormalClosure).OrTimeout(); await server.OrTimeout(); await pair.ServerSocket.CloseAsync(WebSocketCloseStatus.NormalClosure).OrTimeout(); await client.OrTimeout(); // Assert Assert.True(pongFrame.EndOfMessage); Assert.Equal(WebSocketOpcode.Pong, pongFrame.Opcode); Assert.Equal(payload, pongFrame.Payload.ToArray()); } } } } }