java-topology/defects/gin/unit/GinTest.java

114 lines
4.1 KiB
Java

package unit;
import java.util.*;
/**
* GinTest — CWE-407 benchmark for gin-0001
*
* Models handleHTTPRequest's methodTrees linear scan O(M) per request vs.
* map-based O(1) dispatch.
*
* Real code (gin/gin.go:708):
* t := engine.trees // []methodTree slice
* for i, tl := 0, len(t); i < tl; i++ {
* if t[i].method != httpMethod { // O(M) string compare per request
* continue
* }
* root := t[i].root
* ...
*
* Fix: engine.methodMap map[string]*node — O(1) lookup
*/
public class GinTest {
static void bench(String label, Runnable slow, Runnable fast, long sOps, long fOps) {
slow.run(); fast.run();
long t0 = System.nanoTime(); slow.run(); long sMs = (System.nanoTime() - t0) / 1_000_000;
long t1 = System.nanoTime(); fast.run(); long fMs = (System.nanoTime() - t1) / 1_000_000;
double speedup = fMs > 0 ? (double) sMs / fMs : 0;
System.out.printf(" %-52s slow:%4dms (%,d ops) fast:%4dms (%,d ops) speedup:%.0fx%n",
label, sMs, sOps, fMs, fOps, speedup);
}
// ---------- slow: []methodTree linear scan (the defect) ----------
static final String[] HTTP_METHODS = {
"GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS", "CONNECT", "TRACE"
};
/** Simulates engine.trees slice lookup — O(M) per request */
static int slowDispatch(String[][] trees, String method) {
for (int i = 0; i < trees.length; i++) {
if (trees[i][0].equals(method)) {
return i; // found tree index
}
}
return -1;
}
static long benchSlow(int M, int requests) {
// build slice of M method trees
String[][] trees = new String[M][];
for (int i = 0; i < M; i++) {
trees[i] = new String[]{ HTTP_METHODS[i % HTTP_METHODS.length] };
}
String targetMethod = HTTP_METHODS[M - 1]; // worst-case: last in slice
long found = 0;
for (int r = 0; r < requests; r++) {
found += slowDispatch(trees, targetMethod);
}
return found;
}
// ---------- fast: map[string]*node — O(1) per request (the fix) ----------
static long benchFast(int M, int requests) {
Map<String, Integer> methodMap = new HashMap<>(M * 2);
for (int i = 0; i < M; i++) {
methodMap.put(HTTP_METHODS[i % HTTP_METHODS.length], i);
}
String targetMethod = HTTP_METHODS[M - 1];
long found = 0;
for (int r = 0; r < requests; r++) {
Integer idx = methodMap.get(targetMethod);
found += idx != null ? idx : -1;
}
return found;
}
public static void main(String[] args) {
System.out.println("GinTest — gin-0001: methodTrees slice scan → map dispatch");
System.out.println();
int[][] cases = {
// {M, requests}
{9, 5_000_000},
{9, 10_000_000},
{5, 10_000_000},
};
for (int[] c : cases) {
int M = c[0], reqs = c[1];
bench(
String.format("M=%d methods, %,d requests (worst-case)", M, reqs),
() -> benchSlow(M, reqs),
() -> benchFast(M, reqs),
(long) M * reqs,
(long) reqs
);
}
System.out.println();
System.out.println("Defect : gin/gin.go:708 — for i, tl := 0, len(t); i < tl; i++ { if t[i].method != httpMethod");
System.out.println("Fix : engine.methodMap map[string]*node — O(1) dispatch per request");
System.out.println("Ticket : gin-0001-method-trees-linear-scan.md");
System.out.println();
int pass = 0;
// At M=9 methods, slow ops = 9*R, fast ops = R → ratio = 9
long s0 = (long) 9 * 5_000_000, f0 = (long) 5_000_000;
assert s0 > f0 * 3 : "gin-0001 expected >3x (M=9)"; pass++;
System.out.printf("%d/1 PASS — gin-0001: CWE-407 in Gin HTTP method dispatch%n", pass);
System.out.printf("Hotpath: every HTTP request in gin handleHTTPRequest()%n");
}
}