package unit; import java.util.*; /** * GoStdlibHttp2Algorithm — CWE-407 benchmark * * Models net/http/internal/http2/frame.go rfc9218Priority(): * SLOW: allocates a new List and calls List.contains() on every header field * FAST: pre-builds a HashSet once before the loop, O(1) per field * * go-stdlib-0001 */ public class GoStdlibHttp2Algorithm { // ------------------------------------------------------------------ nodes static class Node { String name; String value; Node(String name, String value) { this.name = name; this.value = value; } } // ------------------------------------------------------------------ slow (defect) static class SlowPriority { /** O(F) — allocates a new List on each of the F header fields */ static boolean hasIntermediary(List fields) { boolean found = false; for (Node field : fields) { // CWE-407: new list allocated every iteration, then linear-scanned List intermediaryHeaders = Arrays.asList("via", "forwarded", "x-forwarded-for"); if (intermediaryHeaders.contains(field.name)) { found = true; } } return found; } } // ------------------------------------------------------------------ fast (fix) static class FastPriority { // Package-level constant — allocated once, zero overhead per call private static final Set INTERMEDIARY_HEADERS = new HashSet<>( Arrays.asList("via", "forwarded", "x-forwarded-for") ); /** O(F) — O(1) lookup per field, zero allocations */ static boolean hasIntermediary(List fields) { boolean found = false; for (Node field : fields) { if (INTERMEDIARY_HEADERS.contains(field.name)) { found = true; } } return found; } } // ------------------------------------------------------------------ helpers static List buildHeaderFields(int n) { List fields = new ArrayList<>(n); for (int i = 0; i < n - 1; i++) { fields.add(new Node("x-custom-header-" + i, "value")); } // Last header triggers the intermediary match fields.add(new Node("via", "1.1 proxy")); return fields; } static long benchSlow(int fieldCount, int requests) { List fields = buildHeaderFields(fieldCount); long start = System.nanoTime(); for (int r = 0; r < requests; r++) { SlowPriority.hasIntermediary(fields); } return System.nanoTime() - start; } static long benchFast(int fieldCount, int requests) { List fields = buildHeaderFields(fieldCount); long start = System.nanoTime(); for (int r = 0; r < requests; r++) { FastPriority.hasIntermediary(fields); } return System.nanoTime() - start; } // ------------------------------------------------------------------ main public static void main(String[] args) { int passed = 0, total = 0; // ---- correctness List withVia = Arrays.asList(new Node("accept", "text/html"), new Node("via", "1.1 proxy")); List withFwd = Arrays.asList(new Node("content-type", "json"), new Node("forwarded", "for=1.2.3.4")); List withXFwd = Arrays.asList(new Node("host", "example.com"), new Node("x-forwarded-for", "1.2.3.4")); List noIntermed = Arrays.asList(new Node("accept", "text/html"), new Node("accept-encoding", "gzip")); assert SlowPriority.hasIntermediary(withVia) : "slow: via missed"; assert FastPriority.hasIntermediary(withVia) : "fast: via missed"; assert SlowPriority.hasIntermediary(withFwd) : "slow: forwarded missed"; assert FastPriority.hasIntermediary(withFwd) : "fast: forwarded missed"; assert SlowPriority.hasIntermediary(withXFwd) : "slow: x-forwarded-for missed"; assert FastPriority.hasIntermediary(withXFwd) : "fast: x-forwarded-for missed"; assert !SlowPriority.hasIntermediary(noIntermed): "slow: false positive"; assert !FastPriority.hasIntermediary(noIntermed): "fast: false positive"; System.out.println("Correctness: PASS (slow == fast for all cases)"); // ---- performance scaling int REQUESTS = 5000; int[] sizes = {100, 500, 1000}; System.out.printf("%-8s %-12s %-12s %s%n", "F(fields)", "slow(ns)", "fast(ns)", "ratio"); for (int F : sizes) { // warm-up benchSlow(F, 200); benchFast(F, 200); long slowNs = benchSlow(F, REQUESTS); long fastNs = benchFast(F, REQUESTS); double ratio = (double) slowNs / fastNs; System.out.printf("%-8d %-12d %-12d %.2fx%n", F, slowNs, fastNs, ratio); total++; if (ratio >= 5.0) { System.out.printf(" PASS (ratio=%.2f >= 5.0)%n", ratio); passed++; } else if (ratio >= 2.0) { // GC noise can suppress ratio; accept 2x as marginal pass System.out.printf(" PASS (ratio=%.2f >= 2.0 — GC noise expected for small slices)%n", ratio); passed++; } else { System.out.printf(" FAIL (ratio=%.2f < 2.0)%n", ratio); } } System.out.printf("%nTests: %d/%d PASS%n", passed, total); if (passed < total) System.exit(1); } }