Skip to content

Commit

Permalink
feat: Add graceful http server termination
Browse files Browse the repository at this point in the history
  • Loading branch information
nklomp committed Aug 8, 2023
1 parent 27c8a3e commit bba073b
Show file tree
Hide file tree
Showing 4 changed files with 139 additions and 32 deletions.
29 changes: 16 additions & 13 deletions packages/ssi-express-support/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
"build:clean": "tsc --build --clean && tsc --build"
},
"dependencies": {
"http-terminator": "^3.2.0",
"body-parser": "^1.20.2",
"qs": "^6.11.2",
"uint8arrays": "^3.1.1",
Expand All @@ -20,6 +21,21 @@
"express-session": "^1.17.3",
"passport": "^0.6.0"
},
"devDependencies": {
"@types/http-terminator": "^2.0.2",
"@types/morgan": "^1.9.4",
"@types/qs": "^6.9.7",
"@types/body-parser": "^1.19.2",
"@types/cors": "^2.8.13",
"@types/dotenv-flow": "^3.2.0",
"@types/express": "^4.17.17",
"@types/express-serve-static-core": "^4.17.35",
"@types/express-session": "^1.17.7",
"@types/passport": "^1.0.12",
"@types/passport-http-bearer": "^1.0.37",
"@types/passport-azure-ad": "^4.3.1",
"typescript": "4.9.5"
},
"peerDependencies": {
"passport-azure-ad": "^4.3.5",
"passport-http-bearer": "^1.0.1",
Expand All @@ -36,19 +52,6 @@
"optional": true
}
},
"devDependencies": {
"@types/morgan": "^1.9.4",
"@types/qs": "^6.9.7",
"@types/body-parser": "^1.19.2",
"@types/cors": "^2.8.13",
"@types/dotenv-flow": "^3.2.0",
"@types/express": "^4.17.17",
"@types/express-serve-static-core": "^4.17.35",
"@types/express-session": "^1.17.7",
"@types/passport": "^1.0.12",
"@types/passport-http-bearer": "^1.0.37",
"@types/passport-azure-ad": "^4.3.1"
},
"files": [
"dist/**/*",
"src/**/*",
Expand Down
30 changes: 23 additions & 7 deletions packages/ssi-express-support/src/express-builders.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import express, { Express } from 'express'
import { Application, ApplicationRequestHandler } from 'express-serve-static-core'
import session from 'express-session'
import http from 'http'
import { createHttpTerminator, HttpTerminator } from 'http-terminator'
import morgan from 'morgan'
import passport, { InitializeOptions } from 'passport'
import { checkUserIsInRole } from './auth-utils'
Expand Down Expand Up @@ -37,6 +38,8 @@ export class ExpressBuilder {
private _passportInitOpts?: InitializeOptions
private _userIsInRole?: string | string[]
private _enforcer?: Enforcer
private _server?: http.Server | undefined
private _terminator?: HttpTerminator
private _morgan?: Handler<any, any> | undefined

private constructor(opts?: { existingExpress?: Express; envVarPrefix?: string }) {
Expand Down Expand Up @@ -134,7 +137,13 @@ export class ExpressBuilder {
}

public startListening(express: Express) {
return express.listen(this.getPort(), this.getHostname(), this.listenCallback)
this._server = express.listen(this.getPort(), this.getHostname(), this.listenCallback)
this._terminator = createHttpTerminator({
server: this._server,
// gracefulTerminationTimeout: 10
})

return { server: this._server, terminator: this._terminator }
}

public getHostname(): string {
Expand Down Expand Up @@ -180,11 +189,12 @@ export class ExpressBuilder {
}): ExpressSupport {
const express = this.buildExpress(opts)
const startListening = opts?.startListening === undefined ? this._startListen !== true : opts.startListening
let started = false
if (startListening) {
let started = this._server !== undefined
if (startListening && !started) {
this.startListening(express)
started = true
}

return {
express,
port: this.getPort(),
Expand All @@ -199,15 +209,20 @@ export class ExpressBuilder {
if (!started) {
this.startListening(express)
started = true
} else {
throw Error('Express already started during build')
}
}

if (opts?.disableErrorHandler !== true) {
express.use(jsonErrorHandler)
}
return express
return { server: this._server!, terminator: this._terminator! }
},
stop: async (terminator?: HttpTerminator) => {
const term = terminator ?? this._terminator
if (!term) {
return false
}
return await term.terminate().then(() => true)
},
}
}
Expand Down Expand Up @@ -259,7 +274,8 @@ export class ExpressCorsConfigurer {
private readonly _express?: Express
private readonly _envVarPrefix?: string

constructor({ existingExpress, envVarPrefix }: { existingExpress?: Express; envVarPrefix?: string }) {
constructor(args?: { existingExpress?: Express; envVarPrefix?: string }) {
const { existingExpress, envVarPrefix } = args ?? {}
this._express = existingExpress
this._envVarPrefix = envVarPrefix
}
Expand Down
6 changes: 5 additions & 1 deletion packages/ssi-express-support/src/types.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { Enforcer } from 'casbin'
import { Express, RequestHandler } from 'express'
import { ParamsDictionary } from 'express-serve-static-core'
import http from 'http'
import { HttpTerminator } from 'http-terminator'
import { Strategy } from 'passport'
import { ParsedQs } from 'qs'

Expand Down Expand Up @@ -33,8 +35,10 @@ export interface ExpressSupport {
hostname: string
userIsInRole?: string | string[]
startListening: boolean
server?: http.Server
enforcer?: Enforcer
start: (opts?: { disableErrorHandler?: boolean; doNotStartListening?: boolean }) => Express
start: (opts?: { disableErrorHandler?: boolean; doNotStartListening?: boolean }) => { server: http.Server; terminator: HttpTerminator }
stop: (terminator?: HttpTerminator) => Promise<boolean>
}

export interface ISingleEndpointOpts extends GenericAuthArgs {
Expand Down
Loading

0 comments on commit bba073b

Please sign in to comment.