package dev.coph.nextusweb.server.websocket; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; /** * A trie-based router that maps WebSocket upgrade paths to {@link WebSocketHandler}s. * *
It mirrors the HTTP {@link dev.coph.nextusweb.server.router.Router Router} but is simpler: * a path resolves to a single handler (there is no HTTP method dimension) and only static and * {@code {param}} path-parameter segments are supported (no wildcards). Registration mutates the * shared trie at start-up; {@link #resolve(String)} is safe to call concurrently afterwards.
*/ public final class WebSocketRouter { /** Root of the routing trie. */ private final Node root = new Node(); /** * Creates an empty WebSocket router with no registered handlers. */ public WebSocketRouter() { } /** * Registers a handler at the given path, creating any missing trie nodes. Segments wrapped * in braces (e.g. {@code /chat/{room}}) are treated as path parameters. * * @param path the WebSocket path to mount the handler at * @param handler the handler to invoke for connections on that path * @return this router, for fluent chaining */ public WebSocketRouter on(String path, WebSocketHandler handler) { Node node = root; for (String segment : split(path)) { if (segment.startsWith("{") && segment.endsWith("}")) { if (node.paramChild == null) { node.paramChild = new Node(); node.paramName = segment.substring(1, segment.length() - 1); } node = node.paramChild; } else { node = node.children.computeIfAbsent(segment, k -> new Node()); } } node.handler = handler; return this; } /** * Resolves a path to its handler, capturing any path parameters along the way. * * @param path the request path * @return a {@link Resolution} carrying the handler and captured parameters, or {@code null} * if no handler is registered for the path */ public Resolution resolve(String path) { Map