78 lines
3.4 KiB
C#
78 lines
3.4 KiB
C#
using System.Net;
|
|
using System.Runtime.InteropServices;
|
|
using System.Security.Cryptography;
|
|
using System.Security.Cryptography.X509Certificates;
|
|
using Fleet.Core;
|
|
|
|
namespace MoShiro.Measurement.Tests;
|
|
|
|
public sealed class MsQuicConnectionTests
|
|
{
|
|
[Theory]
|
|
[InlineData(1200, true)]
|
|
[InlineData(1280, true)]
|
|
[InlineData(1472, false)]
|
|
public void DatagramState_UsesTheNativeUint16Alignment(int maximum, bool enabled)
|
|
{
|
|
nint nativeEvent = Marshal.AllocHGlobal(IntPtr.Size + 4);
|
|
try
|
|
{
|
|
Marshal.WriteByte(nativeEvent, IntPtr.Size, enabled ? (byte)1 : (byte)0);
|
|
Marshal.WriteByte(nativeEvent, IntPtr.Size + 1, 0xA5); // padding is not part of MaxSendLength
|
|
Marshal.WriteInt16(nativeEvent, IntPtr.Size + 2, (short)maximum);
|
|
Assert.Equal((enabled, maximum), MsQuicConnection.ReadDatagramState(nativeEvent));
|
|
}
|
|
finally
|
|
{
|
|
Marshal.FreeHGlobal(nativeEvent);
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public async Task DatagramConnection_CanSendCloseAndReconnect_WithoutBlockingNativeWorkers()
|
|
{
|
|
using ECDsa key = ECDsa.Create(ECCurve.NamedCurves.nistP256);
|
|
var request = new CertificateRequest("CN=localhost", key, HashAlgorithmName.SHA256);
|
|
using X509Certificate2 certificate = request.CreateSelfSigned(DateTimeOffset.UnixEpoch, DateTimeOffset.UnixEpoch.AddYears(100));
|
|
await using var listener = new MsQuicListener(IPAddress.Loopback, [0], certificate,
|
|
(connection, data) => connection.Send(data), NullNodeLog.Instance, null);
|
|
listener.Start();
|
|
|
|
for (int attempt = 0; attempt < 3; attempt++)
|
|
{
|
|
var reply = new TaskCompletionSource<byte[]>(TaskCreationOptions.RunContinuationsAsynchronously);
|
|
using var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(10));
|
|
MsQuicConnection client = await MsQuicConnection.ConnectAsync(IPAddress.Loopback, listener.BoundPorts[0], 0,
|
|
key.ExportSubjectPublicKeyInfo(), (_, data) => reply.TrySetResult(data.ToArray()), NullNodeLog.Instance, timeout.Token);
|
|
try
|
|
{
|
|
while (!client.IsReady)
|
|
{
|
|
await Task.Delay(10, timeout.Token);
|
|
}
|
|
|
|
Assert.InRange(client.MaxDatagramBytes, 64, 1500);
|
|
byte[] payload = Enumerable.Range(0, 64).Select(i => (byte)i).ToArray();
|
|
// DATAGRAM is unreliable, and the peer can receive the first
|
|
// packet before its handshake-complete notification permits replies.
|
|
while (!reply.Task.IsCompleted)
|
|
{
|
|
Assert.True(client.Send(payload));
|
|
await Task.WhenAny(reply.Task, Task.Delay(20, timeout.Token));
|
|
timeout.Token.ThrowIfCancellationRequested();
|
|
}
|
|
Assert.Equal(payload, await reply.Task.WaitAsync(timeout.Token));
|
|
Assert.NotNull(client.TransportSnapshot);
|
|
}
|
|
finally
|
|
{
|
|
// The old callback called RegistrationClose, deadlocking its own
|
|
// worker. A second connection then hung in SetParam/GetParam.
|
|
await client.DisposeAsync().AsTask().WaitAsync(TimeSpan.FromSeconds(3));
|
|
await client.WaitForShutdownAsync().WaitAsync(TimeSpan.FromSeconds(3));
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|