Skip to main content
Glama
port-allocator.ts4.72 kB
/** * Port Allocator * * Manages dynamic port allocation for test containers. * Uses Node.js net module to check port availability and tracks * reserved ports to prevent conflicts within the same process. * * @module containers/port-allocator */ import * as net from "net"; import type { IPortAllocator } from "./types"; /** * Error thrown when no available port can be found in the specified range. */ export class PortAllocationError extends Error { constructor( public readonly startPort: number, public readonly endPort: number, public readonly attemptedPorts: number[] ) { super( `No available port found in range ${startPort}-${endPort}. ` + `Attempted ports: ${attemptedPorts.join(", ")}` ); this.name = "PortAllocationError"; } } /** * PortAllocator manages dynamic port allocation for test containers. * * This class provides: * - Port availability checking using Node.js net module * - Finding available ports within a specified range * - Tracking reserved ports to prevent conflicts within the same process * * @implements {IPortAllocator} */ export class PortAllocator implements IPortAllocator { /** * Set of ports reserved by this process. * Used to prevent allocating the same port twice within a single process. */ private readonly reservedPorts: Set<number> = new Set(); /** * Checks if a specific port is available for binding. * * This method attempts to create a TCP server on the specified port. * If successful, the port is available. If it fails with EADDRINUSE, * the port is already in use. * * @param port - The port number to check * @returns Promise resolving to true if the port is available */ async isPortAvailable(port: number): Promise<boolean> { // First check if we've reserved this port if (this.reservedPorts.has(port)) { return false; } return new Promise((resolve) => { const server = net.createServer(); server.once("error", (err: NodeJS.ErrnoException) => { if (err.code === "EADDRINUSE") { resolve(false); } else { // Other errors (e.g., EACCES for privileged ports) also mean unavailable resolve(false); } }); server.once("listening", () => { // Port is available, close the server server.close(() => { resolve(true); }); }); // Try to listen on the port on all interfaces (0.0.0.0) // This matches how Docker binds ports by default server.listen(port, "0.0.0.0"); }); } /** * Finds an available port within the specified range. * * Searches sequentially from startPort to endPort until an available * port is found. Throws PortAllocationError if no port is available. * * @param startPort - The beginning of the port range (inclusive) * @param endPort - The end of the port range (inclusive) * @returns Promise resolving to an available port number * @throws {PortAllocationError} If no available port is found in the range */ async findAvailablePort(startPort: number, endPort: number): Promise<number> { const attemptedPorts: number[] = []; for (let port = startPort; port <= endPort; port++) { attemptedPorts.push(port); const available = await this.isPortAvailable(port); if (available) { return port; } } throw new PortAllocationError(startPort, endPort, attemptedPorts); } /** * Reserves a port to prevent it from being allocated again. * * This is used to track ports that have been allocated by this process * but may not yet be bound by a container. Call this after successfully * allocating a port to prevent double-allocation. * * @param port - The port number to reserve */ reservePort(port: number): void { this.reservedPorts.add(port); } /** * Releases a previously reserved port. * * Call this when a container using the port has been stopped, * allowing the port to be reused. * * @param port - The port number to release */ releasePort(port: number): void { this.reservedPorts.delete(port); } /** * Gets the set of currently reserved ports. * * Useful for debugging and testing. * * @returns A copy of the reserved ports set */ getReservedPorts(): Set<number> { return new Set(this.reservedPorts); } /** * Clears all reserved ports. * * Useful for cleanup during testing. */ clearReservedPorts(): void { this.reservedPorts.clear(); } } /** * Creates a new PortAllocator instance. */ export function createPortAllocator(): IPortAllocator { return new PortAllocator(); }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/keyurgolani/ThoughtMcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server