Skip to content

Commit 85e3fd7

Browse files
committed
feat: make close function as public
The goal is to allow end-user to close the server and trigger the close promises callbacks. Issue: #25
1 parent e5d5fd8 commit 85e3fd7

File tree

6 files changed

+36
-16
lines changed

6 files changed

+36
-16
lines changed

src/core/improvedServer.ts

Lines changed: 30 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
11
import type { Server } from "#interface/server";
22
import type { IStatus } from "#interface/status";
33
import type http from "node:http";
4+
import { exit } from "node:process";
45
import config from "#config/index";
56
import SocketsPool from "#core/socketsPool";
7+
import State from "#core/state";
68
import onRequest from "#util/onRequest";
9+
import sleep from "#util/sleep";
710

811
const { livenessEndpoint, readinessEndpoint } = config;
912

10-
const improvedServer = <TServer extends Server>(
11-
server: TServer,
12-
serverStatus: IStatus,
13-
): TServer & { stop: () => Promise<void> } => {
13+
const improvedServer = <TServer extends Server>(server: TServer, serverStatus: IStatus) => {
1414
const { healthCheck, kubernetes } = config;
1515
const socketsPool = SocketsPool();
1616
const secureSocketsPool = SocketsPool();
@@ -65,28 +65,47 @@ const improvedServer = <TServer extends Server>(
6565
);
6666
}
6767

68-
const stop = async (): Promise<void> => {
68+
const stop = async (
69+
args: { value: number; body?: Error; type?: string } = { value: 0 },
70+
): Promise<void> => {
6971
if (!server.listening || stopping) {
7072
return;
7173
}
7274

7375
stopping = true;
7476

77+
const { timeout, closePromises } = config;
78+
79+
let error: Error | undefined;
80+
if (args.body && args.body.message) {
81+
error = args.body;
82+
} else if (args.type) {
83+
error = new Error(args.type);
84+
}
85+
86+
serverStatus.set(State.SHUTTING_DOWN, error);
87+
88+
await sleep(timeout);
89+
90+
await Promise.all(closePromises.map((closePromise) => closePromise()));
91+
7592
server.removeAllListeners("request");
7693
server.on("request", (_: http.IncomingMessage, res: http.ServerResponse) => {
7794
if (!res.headersSent) res.setHeader("connection", "close");
7895
});
7996

8097
await Promise.all([socketsPool.closeAll(), secureSocketsPool.closeAll()]);
8198

82-
return new Promise((resolve, reject) => {
83-
server.close((error) => {
84-
if (error) {
85-
reject(error);
86-
} else {
87-
resolve();
99+
await new Promise((resolve, reject) => {
100+
server.close((err) => {
101+
if (err) {
102+
return reject(err);
88103
}
104+
return resolve(error);
89105
});
106+
107+
serverStatus.set(State.SHUTDOWN, error);
108+
exit(args.value);
90109
});
91110
};
92111

src/core/index.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import { EventEmitter } from "events";
44
import ImprovedServer from "#core/improvedServer";
55
import Status from "#core/status";
66
import init from "#util/init";
7-
import shutdown from "#util/shutdown";
87

98
const core = (server: Server): ICore => {
109
const _emitter = new EventEmitter();
@@ -15,8 +14,8 @@ const core = (server: Server): ICore => {
1514
init: function () {
1615
return init(this);
1716
},
18-
shutdown: function (type: string, value: number, error?: Error) {
19-
return shutdown(_server, this)(type, value, error);
17+
stop: function (args?: { type?: string; value: number; body?: Error }) {
18+
return _server.stop(args);
2019
},
2120
// eslint-disable-next-line @typescript-eslint/no-explicit-any
2221
on: (name: string, callback: (...args: any[]) => void) => _emitter.on(name, callback),

src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ const buildGracefulServer = (server: Server, options?: IGracefulServerOptions):
1212
isReady: () => gracefulServer.status.isReady(),
1313
setReady: () => gracefulServer.status.setReady(),
1414
on: gracefulServer.on,
15+
close: () => gracefulServer.stop(),
1516
};
1617
};
1718

src/interface/core.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import type { EventEmitter } from "events";
44
export type ICore = {
55
status: IStatus;
66
init: () => ICore;
7-
shutdown: (type: string, value: number, error?: Error) => Promise<void>;
7+
stop: (args?: { type?: string; value: number; body?: Error }) => Promise<void>;
88
// eslint-disable-next-line @typescript-eslint/no-explicit-any
99
on: (name: string, callback: (...args: any[]) => void) => EventEmitter;
1010
};

src/interface/gracefulServer.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,5 @@ export type IGracefulServer = {
55
setReady: () => void;
66
// eslint-disable-next-line @typescript-eslint/no-explicit-any
77
on: (name: string, callback: (...args: any[]) => void) => EventEmitter;
8+
close: () => Promise<void>;
89
};

src/util/init.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import signals from "#core/signals";
66
const init = (parent: ICore) => {
77
for (const signal of signals) {
88
(process as NodeJS.EventEmitter).on(signal.type, async (body) => {
9-
await parent.shutdown(signal.type, signal.code, body);
9+
await parent.stop({ type: signal.type, value: signal.code, body });
1010
});
1111
}
1212
return parent;

0 commit comments

Comments
 (0)