measurement: name the kinds by protocol and action, add the http_request kind and QUIC settings

This commit is contained in:
justme committed 2026-10-22 07:02:23 +08:00
1 parent bf2a598891
commit e77880d372
31 files changed
+938 -141

No files matched your search

@@ -0,0 +1,110 @@
using System.Buffers.Binary;
namespace MoShiro.Measurement;
/// <summary>
/// 数据通道 QUIC 连接的传输参数(<c>agent.json</c> 的 <c>quic</c> 段);null 的项用 MsQuic 默认值。
/// 一条 QUIC 连接由这个 Agent 与对端之间的所有任务共用,所以这些参数属于节点,不属于某个任务。
/// 监听方用它接受连接,拨号方用它发起连接,两边各自生效。
/// </summary>
public sealed record QuicSettings
{
/// <summary>MsQuic 默认的最小、最大 MTU 是 1248 和 1500(IP 包大小,含 IP 和 UDP 头)。</summary>
public const int MinAllowedMtu = 1200;
public const int MaxAllowedMtu = 65535;
/// <summary>路径 MTU 探测的起点;链路的真实 MTU 更小时探测会往下收缩,更大时往上探测到 <see cref="MaxMtu"/>。</summary>
public int? MinMtu { get; init; }
/// <summary>路径 MTU 探测的上限。设成链路的真实 MTU 可以省掉探测,设小可以避开会丢大包的路径。</summary>
public int? MaxMtu { get; init; }
public bool? Pacing { get; init; }
public int? IdleTimeoutMs { get; init; }
public int? KeepAliveMs { get; init; }
public bool IsDefault => MinMtu is null && MaxMtu is null && Pacing is null && IdleTimeoutMs is null && KeepAliveMs is null;
/// <summary>参数是否合法;不合法返回原因。</summary>
public string? Validate()
{
if (MinMtu is { } min && min is < MinAllowedMtu or > MaxAllowedMtu)
{
return $"min_mtu must be {MinAllowedMtu}..{MaxAllowedMtu}";
}
if (MaxMtu is { } max && max is < MinAllowedMtu or > MaxAllowedMtu)
{
return $"max_mtu must be {MinAllowedMtu}..{MaxAllowedMtu}";
}
if (MinMtu is { } lo && MaxMtu is { } hi && lo > hi)
{
return "min_mtu must not exceed max_mtu";
}
if (IdleTimeoutMs is < 0)
{
return "idle_timeout_ms must not be negative";
}
return KeepAliveMs is < 0 ? "keep_alive_ms must not be negative" : null;
}
// QUIC_SETTINGS(msquic.h)的布局:IsSetFlags(u64) 后面依次是 u64、u32、u16 各一组字段,再是位域字节。
// 这里只写用到的几项,其余字段保持 0 且不置位。长度只算到 MtuDiscoveryMissingProbeCount,更靠后的字段 MsQuic 不会读。
private const int Size = 112;
private const int IdleTimeoutOffset = 24;
private const int KeepAliveOffset = 88;
private const int MinMtuOffset = 102;
private const int MaxMtuOffset = 104;
private const int BitFieldOffset = 106;
private const int PacingBit = 1 << 1;
private const int IdleTimeoutFlag = 2;
private const int KeepAliveFlag = 16;
private const int MinMtuFlag = 22;
private const int MaxMtuFlag = 23;
private const int PacingFlag = 25;
internal byte[] ToNative()
{
byte[] native = new byte[Size];
ulong isSet = 0;
if (IdleTimeoutMs is { } idle)
{
BinaryPrimitives.WriteUInt64LittleEndian(native.AsSpan(IdleTimeoutOffset), (ulong)idle);
isSet |= 1UL << IdleTimeoutFlag;
}
if (KeepAliveMs is { } keepAlive)
{
BinaryPrimitives.WriteUInt32LittleEndian(native.AsSpan(KeepAliveOffset), (uint)keepAlive);
isSet |= 1UL << KeepAliveFlag;
}
if (MinMtu is { } min)
{
BinaryPrimitives.WriteUInt16LittleEndian(native.AsSpan(MinMtuOffset), (ushort)min);
isSet |= 1UL << MinMtuFlag;
}
if (MaxMtu is { } max)
{
BinaryPrimitives.WriteUInt16LittleEndian(native.AsSpan(MaxMtuOffset), (ushort)max);
isSet |= 1UL << MaxMtuFlag;
}
if (Pacing is { } pacing)
{
native[BitFieldOffset] = (byte)(pacing ? PacingBit : 0);
isSet |= 1UL << PacingFlag;
}
BinaryPrimitives.WriteUInt64LittleEndian(native, isSet);
return native;
}
}