114 lines
4.1 KiB
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");
|
|
}
|
|
}
|