Skip to content
16 changes: 16 additions & 0 deletions src/__tests__/grpc.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -169,4 +169,20 @@ describe("gRPC Service Integration", () => {
stream.write({ project_id: 1 });
stream.write({ project_id: 2 });
});

it("should route bind failures through the listen error handler", (done) => {
const handleBindError = jest.fn((err: NodeJS.ErrnoException, port: number | string) => {
try {
expect(err).toBeInstanceOf(Error);
expect(err.message).toContain("No address added");
expect(port).toBe(PORT);
duplicateServer.forceShutdown();
done();
} catch (assertionError) {
duplicateServer.forceShutdown();
done(assertionError);
}
});
const duplicateServer = startGrpcServer(PORT, handleBindError);
});
});
11 changes: 8 additions & 3 deletions src/grpc/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import { computeScores } from "../lib/scoring";
import { getTotalProjects } from "../lib/registry";
import { validateApiKey, isRateLimited, incrementUsage } from "../lib/apiKeys";
import { scoreEvents, SCORE_UPDATE_EVENT } from "../lib/events";
import { handleListenError } from "../lib/listen-errors";
import { logger } from "../lib/logger";
import { timingSafeCompare } from "../lib/timing-safe";

const PROTO_PATH = path.join(__dirname, "../proto/heliobond.proto");
Expand Down Expand Up @@ -174,7 +176,10 @@ function chatProjectScores(call: grpc.ServerDuplexStream<any, any>) {
});
}

export function startGrpcServer(port = 50051): grpc.Server {
export function startGrpcServer(
port = 50051,
handleBindError: (err: NodeJS.ErrnoException, port: number | string) => void = handleListenError,
): grpc.Server {
const server = new grpc.Server({
"grpc.keepalive_time_ms": 120000,
"grpc.keepalive_timeout_ms": 20000,
Expand All @@ -190,10 +195,10 @@ export function startGrpcServer(port = 50051): grpc.Server {

server.bindAsync(`0.0.0.0:${port}`, grpc.ServerCredentials.createInsecure(), (err, boundPort) => {
if (err) {
console.error(`[gRPC] Failed to bind to port ${port}:`, err);
handleBindError(err as NodeJS.ErrnoException, port);
return;
}
console.log(`[gRPC] Server running on 0.0.0.0:${boundPort}`);
logger.info(`[gRPC] Server running on 0.0.0.0:${boundPort}`);
});

return server;
Expand Down
Loading