import { IncomingMessage, OutgoingMessage, Server } from "node:http";
import { Socket } from "node:net";
import { INestApplication } from "@nestjs/common";
export class GracefulShutdown {
private connection: Map<any, any>;
private env: string;
private port: number;
private gracefulShutdownTimer: number;
private signals: string[];
constructor() {
this.connection = new Map();
this.env = process.env.NODE_ENV || 'development';
this.port = +process.env.PORT || 3000;
this.gracefulShutdownTimer = this.env === "development" ? 5000 : 10000;
this.signals = [
"SIGTERM",
"SIGINT",
"SIGQUIT",
"SIGHUP",
"SIGABRT",
"SIGALRM",
"SIGUSR1",
"SIGUSR2",
];
}
private sleep(timer: number): Promise<any> {
return new Promise((resolve) => setTimeout(resolve, timer));
}
private close(server: Server, signal: string) {
const socket: Socket = this.connection.get("connection");
if (!socket?.closed) socket?.destroy();
server.close(async (err) => {
await this.sleep(this.gracefulShutdownTimer);
this.connection.clear();
if (!err) {
console.info("Gracefull shutdown successfully");
process.removeAllListeners();
process.kill(process.pid, signal);
process.exit(0);
} else {
console.info("Gracefull shutdown successfully");
process.removeAllListeners();
process.kill(process.pid, signal);
process.exit(0);
}
});
}
listen(app: INestApplication): void {
const server: Server = app.getHttpServer()
server.on("connection", (socket: Socket): void => {
if (socket) this.connection.set("connection", socket);
else socket.destroy();
});
server.on("request", (req: IncomingMessage, _res: OutgoingMessage): void => {
const socketRequest: Socket = req.socket;
const socketConnection: Socket = this.connection.get("connection");
if (!(socketRequest instanceof Socket) || !(socketConnection instanceof Socket)) {
this.connection.clear();
this.connection.set("connection", socketRequest);
}
},
);
this.signals.forEach((event: string) => {
if (this.env === "development") {
process.once(event, (signal: string): void => {
console.info(`Server ${this.env} received signal: ${signal}`);
this.close(server, signal);
});
} else {
process.once(event, (signal: string): void => {
console.info(`Server ${this.env} received signal: ${signal}`);
this.close(server, signal);
});
}
});
const serverInfo: string = `Server is running on port ${this.port}`;
server.listen(this.port, () => console.info(serverInfo));
}
}