11import * as express from "express"
22import * as http from "http"
3+ import * as net from "net"
34import * as nodeFetch from "node-fetch"
45import Websocket from "ws"
56import * as util from "../src/common/util"
@@ -8,13 +9,21 @@ import { handleUpgrade } from "../src/node/wsRouter"
89
910// Perhaps an abstraction similar to this should be used in app.ts as well.
1011export class HttpServer {
11- private hs = http . createServer ( )
12+ private readonly sockets = new Set < net . Socket > ( )
13+ private cleanupTimeout ?: NodeJS . Timeout
1214
13- public constructor ( hs ?: http . Server ) {
14- // See usage in test/integration.ts
15- if ( hs ) {
16- this . hs = hs
17- }
15+ // See usage in test/integration.ts
16+ public constructor ( private readonly hs = http . createServer ( ) ) {
17+ this . hs . on ( "connection" , ( socket ) => {
18+ this . sockets . add ( socket )
19+ socket . on ( "close" , ( ) => {
20+ this . sockets . delete ( socket )
21+ if ( this . cleanupTimeout && this . sockets . size === 0 ) {
22+ clearTimeout ( this . cleanupTimeout )
23+ this . cleanupTimeout = undefined
24+ }
25+ } )
26+ } )
1827 }
1928
2029 /**
@@ -54,13 +63,28 @@ export class HttpServer {
5463 */
5564 public close ( ) : Promise < void > {
5665 return new Promise ( ( res , rej ) => {
66+ // Close will not actually close anything; it just waits until everything
67+ // is closed.
5768 this . hs . close ( ( err ) => {
5869 if ( err ) {
5970 rej ( err )
6071 return
6172 }
6273 res ( )
6374 } )
75+
76+ // If there are sockets remaining we might need to force close them or
77+ // this promise might never resolve.
78+ if ( this . sockets . size > 0 ) {
79+ // Give sockets a chance to close up shop.
80+ this . cleanupTimeout = setTimeout ( ( ) => {
81+ this . cleanupTimeout = undefined
82+ for ( const socket of this . sockets . values ( ) ) {
83+ console . warn ( "a socket was left hanging" )
84+ socket . destroy ( )
85+ }
86+ } , 1000 )
87+ }
6488 } )
6589 }
6690
0 commit comments