diff --git a/tools/RpsLoadProbe/NginxHost.cs b/tools/RpsLoadProbe/NginxHost.cs index 8e6daa18..9997976d 100644 --- a/tools/RpsLoadProbe/NginxHost.cs +++ b/tools/RpsLoadProbe/NginxHost.cs @@ -850,6 +850,16 @@ internal static bool SupportsHttp3Module(string configureOrVersionText) => configureOrVersionText.Contains("http_v3_module", StringComparison.OrdinalIgnoreCase) || configureOrVersionText.Contains("http_quic_module", StringComparison.OrdinalIgnoreCase); + /// + /// True when (or PATH) is nginx 1.25.1+, which can serve + /// prior-knowledge h2c via http2 on without ssl. Ubuntu 24.04's 1.24 cannot. + /// + internal static bool SupportsPriorKnowledgeH2c(string? nginxPath) + { + var exe = ResolveNginxExecutable(nginxPath); + return exe != null && SupportsHttp2OnDirective(ReadVersion(exe)); + } + /// True when the binary supports the http2 on; directive (1.25.1+). internal static bool SupportsHttp2OnDirective(string versionText) { diff --git a/tools/RpsLoadProbe/RampOrchestrator.cs b/tools/RpsLoadProbe/RampOrchestrator.cs index 6b69e3f9..2098b200 100644 --- a/tools/RpsLoadProbe/RampOrchestrator.cs +++ b/tools/RpsLoadProbe/RampOrchestrator.cs @@ -365,11 +365,12 @@ private static List ListArms(RampOptions options, bool applySelection, var haproxyExe = HaproxyHost.ResolveHaproxyExecutable(options.HaproxyPath); var envoyExe = EnvoyHost.ResolveEnvoyExecutable(options.EnvoyPath); var nginxHttp3 = nginxExe != null && NginxHost.SupportsHttp3Module(NginxHost.ReadConfigureArguments(nginxExe)); + var nginxH2c = NginxHost.SupportsPriorKnowledgeH2c(nginxExe); var haproxyQuic = haproxyExe != null && HaproxyHost.SupportsQuic(haproxyExe); var envoyHttp3 = envoyExe != null && EnvoyHost.SupportsHttp3(envoyExe); var bombardierAvailable = BombardierLoadGenerator.IsAvailable(); var arms = ResolveArms(options.Mode, nginxExe != null, nginxHttp3, bombardierAvailable, - haproxyExe != null, haproxyQuic, envoyExe != null, envoyHttp3).ToList(); + haproxyExe != null, haproxyQuic, envoyExe != null, envoyHttp3, nginxH2c).ToList(); if (filterQuic && !System.Net.Quic.QuicListener.IsSupported) { arms = arms.Where(a => @@ -455,8 +456,11 @@ public static async Task RunAsync(RampOptions options, CancellationToken ca await CsvWriter.WriteHeaderAsync(csv); var nginxHttp3 = nginxExe != null && NginxHost.SupportsHttp3Module(NginxHost.ReadConfigureArguments(nginxExe)); + var nginxH2c = NginxHost.SupportsPriorKnowledgeH2c(nginxExe); if (nginxExe != null && !nginxHttp3) ProbeLog.Info("nginx has no http_v3_module — skipping HTTP/3 native reverse arms (install nginx.org mainline on Linux)."); + if (options.Mode == ProbeMode.CompareProductSmoke && nginxExe != null && !nginxH2c) + ProbeLog.Info("nginx is older than 1.25.1 — skipping the prior-knowledge h2c canary (needs http2 on; Ubuntu 24.04 is 1.24)."); var haproxyQuic = haproxyExe != null && HaproxyHost.SupportsQuic(haproxyExe); if (haproxyExe != null && !haproxyQuic) @@ -468,7 +472,7 @@ public static async Task RunAsync(RampOptions options, CancellationToken ca var bombardierAvailable = BombardierLoadGenerator.IsAvailable(); var arms = ResolveArms(options.Mode, nginxExe != null, nginxHttp3, bombardierAvailable, - haproxyExe != null, haproxyQuic, envoyExe != null, envoyHttp3).ToList(); + haproxyExe != null, haproxyQuic, envoyExe != null, envoyHttp3, nginxH2c).ToList(); if (!System.Net.Quic.QuicListener.IsSupported) { var removed = arms.RemoveAll(a => @@ -1061,7 +1065,8 @@ private static void InsertNativeRemainderAfterYarp(List arms, ProbeMode private static IReadOnlyList ResolveArms(ProbeMode mode, bool nginxAvailable, bool nginxHttp3Available = false, bool bombardierAvailable = false, bool haproxyAvailable = false, bool haproxyQuicAvailable = false, - bool envoyAvailable = false, bool envoyHttp3Available = false) + bool envoyAvailable = false, bool envoyHttp3Available = false, + bool nginxH2cAvailable = false) { if (PeerWire.TryGet(mode, out var nativeWire)) return NativeWireAvailable(nativeWire, nginxAvailable, haproxyAvailable, haproxyQuicAvailable, @@ -1358,7 +1363,7 @@ private static IReadOnlyList ResolveArms(ProbeMode mode, bool nginxAvai ..BuildMitmArms(), ..BuildMitmFullArms() ], - ProbeMode.CompareProductSmoke => BuildProductSmokeArms(nginxAvailable, haproxyAvailable, envoyAvailable), + ProbeMode.CompareProductSmoke => BuildProductSmokeArms(haproxyAvailable, envoyAvailable, nginxH2cAvailable), ProbeMode.CompareSpot => BuildSpotArms(), ProbeMode.CompareCeiling => BuildCompareCeilingArms(nginxAvailable, haproxyAvailable, envoyAvailable), ProbeMode.CompareBodies => @@ -1608,8 +1613,8 @@ private static IReadOnlyList BuildSpotArms() /// Minimal arm set for validate-compare-product-gates.ps1 (every Lite/Full/Reverse /// pair the script scores, plus YARP H3 reverse peers). Intended for Mac-only GHA smoke. /// - private static IReadOnlyList BuildProductSmokeArms(bool nginxAvailable, bool haproxyAvailable, - bool envoyAvailable) + private static IReadOnlyList BuildProductSmokeArms(bool haproxyAvailable, + bool envoyAvailable, bool nginxH2cAvailable) { const bool intercept = true; const bool mutate = true; @@ -1663,10 +1668,17 @@ private static IReadOnlyList BuildProductSmokeArms(bool nginxAvailable, }; // Remainder canaries (1a1d78f2) — fail Phase-1 smoke on config typos before full product. - // TWP reverse twins keep each canary in a comparison group (not a singleton peer). - if (nginxAvailable) + // Prior-knowledge h2c needs nginx 1.25.1+ (`http2 on`). A distro 1.24 binary is on PATH + // on ubuntu-latest but cannot serve the wire; scheduling it still records + // twp-reverse-h2c-to-h1, and the MITM gate then fails that wire as a partial pair. + // When the canary does run, include Lite and Full so the reverse row is a complete pair. + if (nginxH2cAvailable) { arms.Add(new("twp-reverse-h2c-to-h1", ProbeMode.ReverseH2cToH1, null)); + arms.Add(new("twp-mitm-h2c-to-h1", ProbeMode.ReverseH2cToH1, null, + EnableHttpInterception: intercept)); + arms.Add(new("twp-mitm-full-h2c-to-h1", ProbeMode.ReverseH2cToH1, null, + EnableHttpInterception: intercept, MutateHttpInterception: mutate)); arms.Add(new("nginx-reverse-h2c-to-h1", ProbeMode.NginxReverseH2cToH1, null)); }