155 lines
5.4 KiB
Java
155 lines
5.4 KiB
Java
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<Worker> workers;
|
|
long scanOps = 0;
|
|
|
|
SlowBalancer(List<Worker> 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<String, Worker> routeMap;
|
|
long scanOps = 0;
|
|
|
|
FastBalancer(List<Worker> 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<Worker> buildChain(int n) {
|
|
List<Worker> 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<Worker> 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<Worker> 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<Worker> 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);
|
|
}
|
|
}
|