Closeable http server
import * as http from 'http' | |
import * as _ from 'lodash' | |
//! In the standard implementation of http server, calling close is not guaranteed to close the | |
//! server. Any idle connections being kept alive by clients will stay open until their timeout | |
//! is reached. This is problematic for gracefully shutting down a process with an http server. | |
//! This function will create an http server that tracks the open connections. On close, idle | |
//! connections are closed and any newly idled connection is immediately closed as well. When there | |
//! are no more connections on the server, the 'empty' event is raised. | |
export function createCloseableHttpServer( | |
requestListener?: (request: http.IncomingMessage, response: http.ServerResponse) => void | |
) { | |
let nextId = 1; | |
let active = true; | |
let connections:any = {}; | |
// create the server | |
let httpServer = http.createServer(requestListener); | |
let origClose = httpServer.close; | |
httpServer.close = close; | |
httpServer.on('connection',onConnection); | |
httpServer.on('request',onRequest); | |
return httpServer; | |
// ---- functions ---- | |
function onConnection(conn:any) { | |
if ( !conn.$$id ) { | |
conn.$$id = nextId++; | |
conn.$$idle = true; | |
connections[conn.$$id] = conn; | |
conn.on('close',() => { | |
delete connections[conn.$$id] | |
emitOnEmpty(); | |
}); | |
} | |
return conn; | |
} | |
function onRequest(req:http.IncomingMessage, res:http.ServerResponse) { | |
// mark the connection as not idle | |
let conn = onConnection(req.socket); | |
conn.$$idle = false; | |
// upon completion, mark the connection as idle | |
res.on('finish',() => { | |
conn.$$idle = true; | |
destroy(conn); | |
}); | |
} | |
function close(cb?:any) { | |
let rval = origClose.apply(httpServer,[cb]); | |
if ( active ) { | |
active = false; | |
_.each(connections,(conn:any) => destroy(conn)); | |
emitOnEmpty(); | |
} | |
return rval; | |
}; | |
function destroy(conn:any) { | |
if ( conn.$$idle && !active ) { | |
conn.destroy(); | |
} | |
} | |
function emitOnEmpty() { | |
if ( !active ) { | |
if ( _.isEmpty(connections) ) { | |
httpServer.emit('empty'); | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment