Skip to content

Commit 6bee6f6

Browse files
authored
Merge pull request #75 from OpenFunction/feature/graceful-shutdown
feat: enable graceful shutdown
2 parents 91ed868 + ee335ba commit 6bee6f6

File tree

4 files changed

+41
-8
lines changed

4 files changed

+41
-8
lines changed

package-lock.json

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
"debug": "^4.3.4",
2020
"express": "^4.18.1",
2121
"express-interceptor": "^1.2.0",
22+
"http-terminator": "^3.2.0",
2223
"lodash": "^4.17.21",
2324
"minimist": "^1.2.6",
2425
"on-finished": "^2.4.1",

src/main.ts

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,17 +16,22 @@
1616

1717
// Functions framework entry point that configures and starts Node.js server
1818
// that runs user's code on HTTP request.
19-
import {getUserFunction} from './loader';
20-
import {ErrorHandler} from './invoker';
21-
import {getServer} from './server';
22-
import {parseOptions, helpText, OptionsError} from './options';
23-
import {OpenFunction} from './functions';
19+
import * as process from 'process';
20+
21+
import {createHttpTerminator} from 'http-terminator';
22+
2423
import getAysncServer from './openfunction/async_server';
2524
import {
2625
OpenFunctionContext,
2726
ContextUtils,
2827
} from './openfunction/function_context';
2928

29+
import {getUserFunction} from './loader';
30+
import {ErrorHandler} from './invoker';
31+
import {getServer} from './server';
32+
import {parseOptions, helpText, OptionsError} from './options';
33+
import {OpenFunction} from './functions';
34+
3035
/**
3136
* Main entrypoint for the functions framework that loads the user's function
3237
* and starts the HTTP server.
@@ -51,6 +56,8 @@ export const main = async () => {
5156
}
5257
const {userFunction, signatureType} = loadedFunction;
5358

59+
// Try to determine the server runtime
60+
// Considering the async runtime in the first place
5461
if (ContextUtils.IsAsyncRuntime(options.context as OpenFunctionContext)) {
5562
options.context!.port = options.port;
5663

@@ -59,7 +66,12 @@ export const main = async () => {
5966
options.context!
6067
);
6168
await server.start();
62-
} else {
69+
70+
// DaprServer uses httpTerminator in server.stop()
71+
handleShutdown(async () => await server.stop());
72+
}
73+
// Then taking sync runtime as the fallback
74+
else {
6375
const server = getServer(userFunction!, signatureType, options.context);
6476
const errorHandler = new ErrorHandler(server);
6577
server
@@ -73,6 +85,12 @@ export const main = async () => {
7385
}
7486
})
7587
.setTimeout(0); // Disable automatic timeout on incoming connections.
88+
89+
// Create and use httpTerminator for Express
90+
const terminator = createHttpTerminator({
91+
server,
92+
});
93+
handleShutdown(async () => await terminator.terminate());
7694
}
7795
} catch (e) {
7896
if (e instanceof OptionsError) {
@@ -86,3 +104,15 @@ export const main = async () => {
86104

87105
// Call the main method to load the user code and start the http server.
88106
main();
107+
108+
function handleShutdown(handler: () => Promise<void>): void {
109+
if (!handler) return;
110+
111+
const shutdown = async (code: string) => {
112+
console.log(`🛑 Terminating OpenFunction server on code ${code}...`);
113+
await handler();
114+
};
115+
116+
process.on('SIGTERM', shutdown);
117+
process.on('SIGINT', shutdown);
118+
}

test/conformance/package.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,12 @@
88
},
99
"scripts": {
1010
"start": "functions-framework --target=writeHttp",
11-
"knative:async": "concurrently npm:knative:async:run:* npm:knative:async:test",
11+
"compile": "cd .. && npm run compile",
12+
"knative:async": "npm run compile && concurrently npm:knative:async:run:* npm:knative:async:test",
1213
"knative:async:run:func": "cross-env DEBUG=test:*,common:*,ofn:* env-cmd -e knative functions-framework --signature-type=openfunction --target=tryKnativeAsync",
1314
"knative:async:run:dapr": "dapr run -H 3500 -d ../data/components/http --log-level warn",
1415
"knative:async:test": "wait-on tcp:3500 tcp:8080 && curl -s -d '{\"data\": \"hello\"}' -H 'Content-Type: application/json' localhost:8080",
15-
"async": "concurrently npm:async:run:*",
16+
"async": "npm run compile && concurrently npm:async:run:*",
1617
"async:run:func": "cross-env DEBUG=test:*,common:*,ofn:* env-cmd -e async functions-framework --target=tryAsync",
1718
"async:run:dapr": "dapr run -H 3500 -p 8080 -d ../data/components/cron --log-level info"
1819
}

0 commit comments

Comments
 (0)