From 24c2a701380d080be937b4dac5cb380a1f030de2 Mon Sep 17 00:00:00 2001 From: Pedro Papadopolis Date: Sat, 16 Dec 2023 18:06:26 +1100 Subject: [PATCH] feat: Make compression middleware optional (#5306) ## Why Currently AWS API Gateway doesn't have compression enabled by default, this PR will make it easier to for example deploy Unleash over to AWS Lambda without further configuration in API Gateway, frameworks like Serverless requires a bit more work to set up compression and some times one might not need compression at all. ## How Create a new config flag called `disableCompression` which will not include `compression` middleware in express' instance when set as true. --- .../__snapshots__/create-config.test.ts.snap | 1 + src/lib/app.test.ts | 47 +++++++++++++++++++ src/lib/app.ts | 5 +- src/lib/create-config.ts | 4 ++ src/lib/types/option.ts | 1 + .../deploy/configuring-unleash.md | 1 + 6 files changed, 58 insertions(+), 1 deletion(-) diff --git a/src/lib/__snapshots__/create-config.test.ts.snap b/src/lib/__snapshots__/create-config.test.ts.snap index e76e1e186f..4830f6ebcd 100644 --- a/src/lib/__snapshots__/create-config.test.ts.snap +++ b/src/lib/__snapshots__/create-config.test.ts.snap @@ -148,6 +148,7 @@ exports[`should create default config 1`] = ` "server": { "baseUriPath": "", "cdnPrefix": undefined, + "disableCompression": false, "enableHeapSnapshotEnpoint": false, "enableRequestLogger": false, "gracefulShutdownEnable": true, diff --git a/src/lib/app.test.ts b/src/lib/app.test.ts index bf860ab2af..9d27f5fd66 100644 --- a/src/lib/app.test.ts +++ b/src/lib/app.test.ts @@ -1,5 +1,12 @@ import express from 'express'; import { createTestConfig } from '../test/config/test-config'; +import compression from 'compression'; + +jest.mock('compression', () => + jest.fn().mockImplementation(() => (req, res, next) => { + next(); + }), +); jest.mock( './routes', @@ -40,3 +47,43 @@ test('should call preRouterHook', async () => { await getApp(config, {}, {}); expect(called).toBe(1); }); + +describe('compression middleware', () => { + beforeAll(() => { + (compression as jest.Mock).mockClear(); + }); + + afterEach(() => { + (compression as jest.Mock).mockClear(); + }); + + test.each([ + { + disableCompression: true, + expectCompressionEnabled: false, + }, + { + disableCompression: false, + expectCompressionEnabled: true, + }, + { + disableCompression: null, + expectCompressionEnabled: true, + }, + { + disableCompression: undefined, + expectCompressionEnabled: true, + }, + ])( + `should expect the compression middleware to be $expectCompressionEnabled when configInput.server.disableCompression is $disableCompression`, + async ({ disableCompression, expectCompressionEnabled }) => { + const config = createTestConfig({ + server: { + disableCompression: disableCompression as any, + }, + }); + await getApp(config, {}, {}); + expect(compression).toBeCalledTimes(+expectCompressionEnabled); + }, + ); +}); diff --git a/src/lib/app.ts b/src/lib/app.ts index d9001411b7..d6851fc4d4 100644 --- a/src/lib/app.ts +++ b/src/lib/app.ts @@ -63,7 +63,10 @@ export default async function getApp( config.preHook(app, config, services, db); } - app.use(compression()); + if (!config.server.disableCompression) { + app.use(compression()); + } + app.use(cookieParser()); app.use((req, res, next) => { diff --git a/src/lib/create-config.ts b/src/lib/create-config.ts index 2787a43b52..4509bf99a9 100644 --- a/src/lib/create-config.ts +++ b/src/lib/create-config.ts @@ -217,6 +217,10 @@ const defaultServerOption: IServerOption = { process.env.ENABLE_HEAP_SNAPSHOT_ENPOINT, false, ), + disableCompression: parseEnvVarBoolean( + process.env.SERVER_DISABLE_COMPRESSION, + false, + ), keepAliveTimeout: secondsToMilliseconds( parseEnvVarNumber(process.env.SERVER_KEEPALIVE_TIMEOUT, 15), ), diff --git a/src/lib/types/option.ts b/src/lib/types/option.ts index 887c88b429..9d353b2f74 100644 --- a/src/lib/types/option.ts +++ b/src/lib/types/option.ts @@ -83,6 +83,7 @@ export interface IServerOption { port?: number; host?: string; pipe?: string; + disableCompression?: boolean; keepAliveTimeout: number; headersTimeout: number; baseUriPath: string; diff --git a/website/docs/using-unleash/deploy/configuring-unleash.md b/website/docs/using-unleash/deploy/configuring-unleash.md index 9cf907ad94..290e670f92 100644 --- a/website/docs/using-unleash/deploy/configuring-unleash.md +++ b/website/docs/using-unleash/deploy/configuring-unleash.md @@ -137,6 +137,7 @@ unleash.start(unleashOptions); - `refreshIntervalInMs` - how often (in milliseconds) front-end clients should refresh their data from the cache. Overridable with the `FRONTEND_API_REFRESH_INTERVAL_MS` environment variable. - **accessControlMaxAge** - You can configure the max-age of the Access-Control-Max-Age header. Defaults to 86400 seconds. Overridable with the `ACCESS_CONTROL_MAX_AGE` environment variable. - **responseTimeWithAppNameKillSwitch** - use this to disable metrics with app names. This is enabled by default but may increase the cardinality of metrics causing Unleash memory usage to grow if your app name is randomly generated (which is not recommended). Overridable with the `UNLEASH_RESPONSE_TIME_WITH_APP_NAME_KILL_SWITCH` environment variable. +- **disableCompression** - Disables Express compression middleware, useful when configuring the application in a serverless environment. Defaults to `false`. Overridable with the `SERVER_DISABLE_COMPRESSION` environment variable. - **keepAliveTimeout** - Use this to tweak connection keepalive timeout in seconds. Useful for hosted situations where you need to make sure your connections are closed before terminating the instance. Defaults to `15`. Overridable with the `SERVER_KEEPALIVE_TIMEOUT` environment variable. You can also set the environment variable `ENABLED_ENVIRONMENTS` to a comma delimited string of environment names to override environments. - **metricsRateLimiting** - Use the following to tweak the rate limits for `/api/client/register`, `/api/client/metrics`, `/api/frontend/register` and `/api/frontend/metrics` POST endpoints