using System.Net; using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; using Fleet.Core; namespace MoShiro.Measurement.Tests; public sealed class QuicSettingsTests { [Theory] [InlineData(1100, null, false)] [InlineData(null, 70_000, false)] [InlineData(1400, 1300, false)] [InlineData(1280, 1500, true)] [InlineData(null, null, true)] public void Validate_RejectsMtuOutsideTheSupportedRangeOrOutOfOrder(int? min, int? max, bool valid) => Assert.Equal(valid, new QuicSettings { MinMtu = min, MaxMtu = max }.Validate() is null); /// 设置真的写进了 MsQuic:最大 MTU 压低后数据报上限随之变小,最小 MTU 抬高后一连上就是大包(不用等路径探测)。 [Theory] [InlineData(1200, 1280, 1200, 1280)] [InlineData(1500, 1500, 1400, 1500)] public async Task Settings_ChangeTheDatagramLimitOfTheConnection(int min, int max, int atLeast, int atMost) { var settings = new QuicSettings { MinMtu = min, MaxMtu = max }; var log = new NullLog(); 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, (_, _) => { }, log, null, settings); listener.Start(); using var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(15)); MsQuicConnection client = await MsQuicConnection.ConnectAsync(IPAddress.Loopback, listener.BoundPorts[0], 0, key.ExportSubjectPublicKeyInfo(), (_, _) => { }, log, timeout.Token, settings); try { while (!client.IsReady) { await Task.Delay(10, timeout.Token); } // 路径 MTU 探测还可能在往上走,给它一点时间;上限始终不能超过设置的最大 MTU await Task.Delay(1500, timeout.Token); Assert.InRange(client.MaxDatagramBytes, atLeast - 100, atMost); } finally { await client.DisposeAsync().AsTask().WaitAsync(TimeSpan.FromSeconds(3)); } } private sealed class NullLog : INodeLog { public void Write(NodeLogLevel level, string message, params (string Key, object? Value)[] fields) { } } }