Skip to content

Instantly share code, notes, and snippets.

@jinxidoru
Last active January 24, 2022 19:31
Show Gist options
  • Star 8 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jinxidoru/0611100d1d12ecddfa04 to your computer and use it in GitHub Desktop.
Save jinxidoru/0611100d1d12ecddfa04 to your computer and use it in GitHub Desktop.
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