package unit; import java.util.*; /** * Models Apache httpd find_route_worker redirect chain traversal. * * SLOW: O(N²) — recursive linear scan of N workers per level of redirect chain. * FAST: O(N) — route→worker HashMap built at init; O(1) lookup per chain step. * * CWE-407: modules/proxy/mod_proxy_balancer.c:210-266 */ public class HttpdProxyRouteWorkerAlgorithmTest { static class Worker { final String route; final String redirect; // null if none boolean usable; Worker(String route, String redirect, boolean usable) { this.route = route; this.redirect = redirect; this.usable = usable; } } // ------------------------------------------------------------------------- // Slow — O(N²) recursive linear scan // ------------------------------------------------------------------------- static class SlowBalancer { final List workers; long scanOps = 0; SlowBalancer(List workers) { this.workers = workers; } Worker findRouteWorker(String route, int recursion) { if (recursion >= workers.size()) return null; for (Worker w : workers) { scanOps++; if (w.route.equals(route)) { if (w.usable) return w; if (w.redirect != null) { return findRouteWorker(w.redirect, recursion + 1); } return null; } } return null; } long resolve(String startRoute) { scanOps = 0; findRouteWorker(startRoute, 0); return scanOps; } } // ------------------------------------------------------------------------- // Fast — O(N) HashMap lookup // ------------------------------------------------------------------------- static class FastBalancer { final Map routeMap; long scanOps = 0; FastBalancer(List workers) { routeMap = new HashMap<>(); for (Worker w : workers) { routeMap.put(w.route, w); scanOps++; // build cost counted separately, not in resolve } } Worker findRouteWorker(String route, int recursion, int maxDepth) { if (recursion >= maxDepth) return null; scanOps++; Worker w = routeMap.get(route); if (w == null) return null; if (w.usable) return w; if (w.redirect != null) return findRouteWorker(w.redirect, recursion + 1, maxDepth); return null; } long resolve(String startRoute, int maxDepth) { scanOps = 0; findRouteWorker(startRoute, 0, maxDepth); return scanOps; } } // ------------------------------------------------------------------------- // Build balancer with redirect chain: w0→w1→w2→...→wN-1(usable) // All workers except last are down+redirect to next // ------------------------------------------------------------------------- static List buildChain(int n) { List workers = new ArrayList<>(); for (int i = 0; i < n; i++) { String route = "w" + i; String redirect = (i < n - 1) ? "w" + (i + 1) : null; boolean usable = (i == n - 1); workers.add(new Worker(route, redirect, usable)); } return workers; } public static void main(String[] args) { int passed = 0, total = 0; int[] sizes = {10, 25, 50, 100}; System.out.println("=== httpd-0004: find_route_worker redirect chain O(N²) ==="); for (int n : sizes) { List workers = buildChain(n); SlowBalancer slow = new SlowBalancer(workers); FastBalancer fast = new FastBalancer(workers); long s = slow.resolve("w0"); long f = fast.resolve("w0", n); double ratio = (double) s / f; total++; boolean ok = s > f && ratio >= 2.0; System.out.printf("N=%3d slow=%6d fast=%3d ratio=%5.1fx %s%n", n, s, f, ratio, ok ? "PASS" : "FAIL"); if (ok) passed++; } // Correctness: both find the final usable worker List chain20 = buildChain(20); SlowBalancer slow20 = new SlowBalancer(chain20); FastBalancer fast20 = new FastBalancer(chain20); Worker sw = slow20.findRouteWorker("w0", 0); Worker fw = fast20.findRouteWorker("w0", 0, 20); total++; boolean correct = sw != null && fw != null && sw.route.equals(fw.route); System.out.printf("correctness (both find w19): %s%n", correct ? "PASS" : "FAIL"); if (correct) passed++; // Ratio check at N=50 >= 5x List chain50 = buildChain(50); SlowBalancer s50 = new SlowBalancer(chain50); FastBalancer f50 = new FastBalancer(chain50); long sOps = s50.resolve("w0"); long fOps = f50.resolve("w0", 50); double ratio50 = (double) sOps / fOps; total++; boolean ratioOk = ratio50 >= 5.0; System.out.printf("N=50 ratio=%.1fx >= 5x: %s%n", ratio50, ratioOk ? "PASS" : "FAIL"); if (ratioOk) passed++; System.out.printf("%n%d/%d PASS%n", passed, total); if (passed < total) System.exit(1); } }