package unit; import java.util.*; /** * haproxy-0004 — CWE-407: http_capture_headers O(H×C) per request * * Models http_ana.c http_capture_headers(): * Slow: for each of H request headers, walk C-entry cap_hdr linked list (O(C)) * → O(H × C) per request * Fast: pre-build HashMap at config time → O(H) per request * * Hot path: called on every HTTP request and response when `capture request header` * directives are configured. */ public class HaproxyCaptureHeadersAlgorithmTest { // Simulates a cap_hdr linked-list entry (from include/haproxy/capture-t.h) static class CapHdr { final String name; final int index; CapHdr next; CapHdr(String name, int index) { this.name = name; this.index = index; } } // Simulates an HTTP header (name + value) static class HttpHeader { final String name; final String value; HttpHeader(String name, String value) { this.name = name; this.value = value; } } // --- SLOW: O(H × C) per request (defect) --- // Models: for each htx header → for each cap_hdr → strncasecmp static long captureHeadersSlow(List headers, CapHdr capHdrHead) { long ops = 0; String[] cap = new String[64]; for (HttpHeader hdr : headers) { for (CapHdr h = capHdrHead; h != null; h = h.next) { ops++; // strncasecmp call if (h.name.equalsIgnoreCase(hdr.name)) { if (cap[h.index] == null) cap[h.index] = hdr.value; } } } return ops; } // --- FAST: O(H) per request (fix) --- // Models: pre-built HashMap at config time static long captureHeadersFast(List headers, Map capHdrMap) { long ops = 0; String[] cap = new String[64]; for (HttpHeader hdr : headers) { ops++; // O(1) hash lookup CapHdr h = capHdrMap.get(hdr.name.toLowerCase()); if (h != null && cap[h.index] == null) cap[h.index] = hdr.value; } return ops; } // Build cap_hdr linked list static CapHdr buildCapHdrList(int count) { CapHdr head = null; for (int i = count - 1; i >= 0; i--) { CapHdr h = new CapHdr("X-Custom-Header-" + i, i); h.next = head; head = h; } return head; } // Build pre-indexed cap_hdr map static Map buildCapHdrMap(CapHdr head) { Map map = new HashMap<>(); for (CapHdr h = head; h != null; h = h.next) map.put(h.name.toLowerCase(), h); return map; } // Build HTTP request headers list static List buildHeaders(int count, int captureHits) { List headers = new ArrayList<>(); for (int i = 0; i < count; i++) { String name; if (i < captureHits) name = "X-Custom-Header-" + i; // will match cap_hdr else name = "Standard-Header-" + i; // won't match cap_hdr headers.add(new HttpHeader(name, "value-" + i)); } return headers; } public static void main(String[] args) { System.out.println("haproxy-0004 CWE-407: http_capture_headers O(H*C) vs O(H)"); System.out.println("=========================================================="); // Parameters: H request headers, C capture headers configured int[][] params = { {30, 10}, {50, 20}, {100, 50} }; for (int[] p : params) { int H = p[0], C = p[1]; CapHdr capHdrHead = buildCapHdrList(C); Map capHdrMap = buildCapHdrMap(capHdrHead); List headers = buildHeaders(H, C / 2); long slowOps = captureHeadersSlow(headers, capHdrHead); long fastOps = captureHeadersFast(headers, capHdrMap); double ratio = (double) slowOps / fastOps; System.out.printf(" H=%3d headers, C=%2d captures: slow=%,5d ops fast=%,3d ops speedup=%.0fx%n", H, C, slowOps, fastOps, ratio); assert ratio >= (double) C / 2 : "Expected speedup >= " + (C/2) + "x but got " + ratio; } System.out.println("\nPASS"); } }