1+ import EventEmitter from "node:events" ;
12import http from "node:http" ;
23import packageJson from "../package.json" with { type : "json" } ;
34import { Request } from "./Request.js" ;
@@ -6,7 +7,11 @@ import {Response} from "./response/Response.js";
67import { RouteRegistry } from "./routing/RouteRegistry.js" ;
78import { 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 */
@@ -15,20 +20,20 @@ class Server {
1520 * This server's route registry.
1621 */
1722 public readonly routes = new RouteRegistry ( ) ;
18- private readonly server : http . Server ;
19- private readonly copyOrigin : boolean ;
20- private readonly handleConditionalRequests : boolean ;
21-
2223 /**
2324 * This server's error registry.
2425 */
2526 public readonly errors = new ServerErrorRegistry ( ) ;
27+ private readonly server : http . Server ;
28+ private readonly copyOrigin : boolean ;
29+ private readonly handleConditionalRequests : boolean ;
2630
2731 /**
2832 * Create a new HTTP server.
2933 * @param options Server options.
3034 */
3135 public constructor ( options : Server . Options ) {
36+ super ( ) ;
3237 this . server = http . createServer ( {
3338 joinDuplicateHeaders : true ,
3439 } , this . listener . bind ( this ) ) ;
@@ -40,14 +45,33 @@ class Server {
4045 this . copyOrigin = options . copyOrigin ?? false ;
4146 this . handleConditionalRequests = options . handleConditionalRequests ?? true ;
4247
43- this . server . listen ( options . port ) ;
48+ this . server . listen ( options . port , process . env . HOST , ( ) => this . emit ( "listening" ) ) ;
49+
50+ this . once ( "listening" , ( ) => {
51+ if ( this . listenerCount ( "error" ) === 0 )
52+ this . on ( "error" , e => console . error ( "Internal Server Error:" , e ) ) ;
53+ } ) ;
4454 }
4555
4656 /** @internal **/
4757 public get _keepAliveTimeout ( ) {
4858 return this . server . keepAliveTimeout ;
4959 }
5060
61+ public async close ( ) : Promise < void > {
62+ this . emit ( "closing" ) ;
63+ await Promise . race ( [
64+ new Promise < void > ( resolve => {
65+ this . server . close ( ( ) => resolve ( ) ) ;
66+ } ) ,
67+ new Promise < void > ( resolve => setTimeout ( ( ) => {
68+ this . server . closeAllConnections ( ) ;
69+ resolve ( ) ;
70+ } , 5000 ) ) ,
71+ ] ) ;
72+ this . emit ( "closed" ) ;
73+ }
74+
5175 private async listener ( req : http . IncomingMessage , res : http . ServerResponse ) {
5276 let apiRequest : Request ;
5377 try {
@@ -79,7 +103,7 @@ class Server {
79103 if ( e instanceof RouteRegistry . NoRouteError )
80104 response = this . errors . _get ( ServerErrorRegistry . ErrorCodes . NO_ROUTE , apiRequest ) ;
81105 else {
82- console . error ( "Internal Server Error: ", e ) ;
106+ this . emit ( "error ", e as any ) ;
83107 response = this . errors . _get ( ServerErrorRegistry . ErrorCodes . INTERNAL , apiRequest ) ;
84108 }
85109 }
@@ -131,18 +155,6 @@ class Server {
131155 . split ( "," )
132156 . map ( t => t . trim ( ) )
133157 }
134-
135- public close ( ) : Promise < void > {
136- return Promise . race ( [
137- new Promise < void > ( resolve => {
138- this . server . close ( ( ) => resolve ( ) ) ;
139- } ) ,
140- new Promise < void > ( resolve => setTimeout ( ( ) => {
141- this . server . closeAllConnections ( ) ;
142- resolve ( ) ;
143- } , 5000 ) ) ,
144- ] ) ;
145- }
146158}
147159
148160namespace Server {
@@ -176,6 +188,33 @@ namespace Server {
176188 */
177189 readonly handleConditionalRequests ?: boolean ;
178190 }
191+
192+ /**
193+ * Server events map
194+ */
195+ export interface Events {
196+ /**
197+ * Server is listening and ready to accept connections.
198+ */
199+ listening : [ void ] ;
200+
201+ /**
202+ * The server is closing and not accepting new connections.
203+ */
204+ closing : [ void ] ;
205+
206+ /**
207+ * All connections have ended and the server has closed.
208+ */
209+ closed : [ void ] ;
210+
211+ /**
212+ * An uncaught error occurred. Client has been sent {@link ServerErrorRegistry.ErrorCodes.INTERNAL} error.
213+ * If no listener is registered when the server begins listening for the first time, a default listener will be
214+ * added to direct errors to stderr.
215+ */
216+ error : [ Error ] ;
217+ }
179218}
180219
181220export { Server } ;
0 commit comments