Skip to content

Commit 4b358e1

Browse files
committed
add server events
1 parent 966d6ea commit 4b358e1

File tree

1 file changed

+43
-5
lines changed

1 file changed

+43
-5
lines changed

src/Server.ts

Lines changed: 43 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import EventEmitter from "node:events";
12
import http from "node:http";
23
import packageJson from "../package.json" with {type: "json"};
34
import {Request} from "./Request.js";
@@ -6,7 +7,11 @@ import {Response} from "./response/Response.js";
67
import {RouteRegistry} from "./routing/RouteRegistry.js";
78
import {ServerErrorRegistry} from "./ServerErrorRegistry.js";
89

9-
class Server {
10+
/**
11+
* An HTTP server.
12+
* @see {@link Server.Events} for events.
13+
*/
14+
class Server extends EventEmitter<Server.Events> {
1015
/**
1116
* Headers sent with every response.
1217
*/
@@ -29,6 +34,7 @@ class Server {
2934
* @param options Server options.
3035
*/
3136
public constructor(options: Server.Options) {
37+
super();
3238
this.server = http.createServer({
3339
joinDuplicateHeaders: true,
3440
}, this.listener.bind(this));
@@ -40,7 +46,12 @@ class Server {
4046
this.copyOrigin = options.copyOrigin ?? false;
4147
this.handleConditionalRequests = options.handleConditionalRequests ?? true;
4248

43-
this.server.listen(options.port);
49+
this.server.listen(options.port, process.env.HOST, () => this.emit("listening"));
50+
51+
this.once("listening", () => {
52+
if (this.listenerCount("error") === 0)
53+
this.on("error", e => console.error("Internal Server Error:", e));
54+
});
4455
}
4556

4657
/** @internal **/
@@ -79,7 +90,7 @@ class Server {
7990
if (e instanceof RouteRegistry.NoRouteError)
8091
response = this.errors._get(ServerErrorRegistry.ErrorCodes.NO_ROUTE, apiRequest);
8192
else {
82-
console.error("Internal Server Error:", e);
93+
this.emit("error", e as any);
8394
response = this.errors._get(ServerErrorRegistry.ErrorCodes.INTERNAL, apiRequest);
8495
}
8596
}
@@ -132,8 +143,9 @@ class Server {
132143
.map(t => t.trim())
133144
}
134145

135-
public close(): Promise<void> {
136-
return Promise.race([
146+
public async close(): Promise<void> {
147+
this.emit("closing");
148+
await Promise.race([
137149
new Promise<void>(resolve => {
138150
this.server.close(() => resolve());
139151
}),
@@ -142,6 +154,7 @@ class Server {
142154
resolve();
143155
}, 5000)),
144156
]);
157+
this.emit("closed");
145158
}
146159
}
147160

@@ -176,6 +189,31 @@ namespace Server {
176189
*/
177190
readonly handleConditionalRequests?: boolean;
178191
}
192+
193+
/**
194+
* Server events map
195+
*/
196+
export interface Events {
197+
/**
198+
* Server is listening and ready to accept connections.
199+
*/
200+
listening: [void];
201+
202+
/**
203+
* The server is closing and not accepting new connections.
204+
*/
205+
closing: [void];
206+
207+
/**
208+
* All connections have ended and the server has closed.
209+
*/
210+
closed: [void];
211+
212+
/**
213+
* An uncaught error occurred. Client has been sent {@link ServerErrorRegistry.ErrorCodes.INTERNAL} error.
214+
*/
215+
error: [Error];
216+
}
179217
}
180218

181219
export {Server};

0 commit comments

Comments
 (0)