import http from 'http' ; import waitFor from 'p-wait-for' ; import type { Duplex , } from 'stream' ; import Logger from '../Logger' ; import type { HttpTerminatorConfigurationInput , InternalHttpTerminator , } from '../types' ; const log = Logger . child ( { namespace : 'createHttpTerminator' , } ) ; const configurationDefaults = { gracefulTerminationTimeout : 1000 , } ; export default ( configurationInput ) : InternalHttpTerminator => { const configuration = { ... configurationDefaults , ... configurationInput , } ; const server = configuration . server ; const sockets = new Set < Duplex > ( ) ; const secureSockets = new Set < Duplex > ( ) ; let terminating ; server . on ( 'connection' , ( socket ) => { if ( terminating ) { socket . destroy ( ) ; } else { sockets . add ( socket ) ; socket . once ( 'close' , ( ) => { sockets . delete ( socket ) ; } ) ; } } ) ; server . on ( 'secureConnection' , ( socket ) => { if ( terminating ) { socket . destroy ( ) ; } else { secureSockets . add ( socket ) ; socket . once ( 'close' , ( ) => { secureSockets . delete ( socket ) ; } ) ; } } ) ; see https const destroySocket = ( socket ) => { socket . destroy ( ) ; if ( socket . server instanceof http . Server ) { sockets . delete ( socket ) ; } else { secureSockets . delete ( socket ) ; } } ; const terminate = async ( ) => { if ( terminating ) { log . warn ( 'already terminating HTTP server' ) ; return terminating ; } let resolveTerminating ; let rejectTerminating ; terminating = new Promise ( ( resolve , reject ) => { resolveTerminating = resolve ; rejectTerminating = reject ; } ) ; server . on ( 'request' , ( incomingMessage , outgoingMessage ) => { if ( ! outgoingMessage . headersSent ) { outgoingMessage . setHeader ( 'connection' , 'close' ) ; } } ) ; for ( const socket of sockets ) { if ( ! ( socket . server instanceof http . Server ) ) { continue ; } const serverResponse = socket . _httpMessage ; if ( serverResponse ) { if ( ! serverResponse . headersSent ) { serverResponse . setHeader ( 'connection' , 'close' ) ; } continue ; } destroySocket ( socket ) ; } for ( const socket of secureSockets ) { const serverResponse = socket . _httpMessage ; if ( serverResponse ) { if ( ! serverResponse . headersSent ) { serverResponse . setHeader ( 'connection' , 'close' ) ; } continue ; } destroySocket ( socket ) ; } try { await waitFor ( ( ) => { return sockets . size === 0 && secureSockets . size === 0 ; } , { interval : 10 , timeout : configuration . gracefulTerminationTimeout , } ) ; } catch { } finally { for ( const socket of sockets ) { destroySocket ( socket ) ; } for ( const socket of secureSockets ) { destroySocket ( socket ) ; } } server . close ( ( error ) => { if ( error ) { rejectTerminating ( error ) ; } else { resolveTerminating ( ) ; } } ) ; return terminating ; } ; return { secureSockets , sockets , terminate , } ; } ;