java-topology/defects/go/unit/GoStdlibHttp2Algorithm.java

144 lines
5.6 KiB
Java

package unit;
import java.util.*;
/**
* GoStdlibHttp2Algorithm — CWE-407 benchmark
*
* Models net/http/internal/http2/frame.go rfc9218Priority():
* SLOW: allocates a new List<String> and calls List.contains() on every header field
* FAST: pre-builds a HashSet<String> 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<String> on each of the F header fields */
static boolean hasIntermediary(List<Node> fields) {
boolean found = false;
for (Node field : fields) {
// CWE-407: new list allocated every iteration, then linear-scanned
List<String> 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<String> INTERMEDIARY_HEADERS = new HashSet<>(
Arrays.asList("via", "forwarded", "x-forwarded-for")
);
/** O(F) — O(1) lookup per field, zero allocations */
static boolean hasIntermediary(List<Node> fields) {
boolean found = false;
for (Node field : fields) {
if (INTERMEDIARY_HEADERS.contains(field.name)) {
found = true;
}
}
return found;
}
}
// ------------------------------------------------------------------ helpers
static List<Node> buildHeaderFields(int n) {
List<Node> 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<Node> 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<Node> 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<Node> withVia = Arrays.asList(new Node("accept", "text/html"), new Node("via", "1.1 proxy"));
List<Node> withFwd = Arrays.asList(new Node("content-type", "json"), new Node("forwarded", "for=1.2.3.4"));
List<Node> withXFwd = Arrays.asList(new Node("host", "example.com"), new Node("x-forwarded-for", "1.2.3.4"));
List<Node> 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);
}
}