diff --git a/.nsprc b/.nsprc new file mode 100644 index 00000000..35398ba5 --- /dev/null +++ b/.nsprc @@ -0,0 +1,12 @@ +{ + "1105443": { + "active": true, + "notes": "Ignored, check back for updated deps", + "expiry": "2026-05-01" + }, + "1105444": { + "active": true, + "notes": "Ignored, check back for updated deps", + "expiry": "2026-05-01" + } +} diff --git a/CHANGELOG.md b/CHANGELOG.md index 59655757..6e752df3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,14 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). +## [Unreleased] - Unreleased + +### Changed + +- Compression is enabled by default, can be disabled by setting + `ENABLE_RESPONSE_COMPRESSION` to `false`. If using post-hooks, you must update + to hooks to handle compression or disable compression. + ## [4.2.0] - 2025-05-05 ### Added diff --git a/README.md b/README.md index af17c06b..7257f420 100644 --- a/README.md +++ b/README.md @@ -609,9 +609,10 @@ There are some settings that should be reviewed and updated as needeed in the se | CORS_CREDENTIALS | Configure whether or not to send the `Access-Control-Allow-Credentials` CORS header. Header will be sent if set to `true`. | none | | CORS_METHODS | Configure whether or not to send the `Access-Control-Allow-Methods` CORS header. Expects a comma-delimited string, e.g., `GET,PUT,POST`. | `GET,HEAD,PUT,PATCH,POST,DELETE` | | CORS_HEADERS | Configure whether or not to send the `Access-Control-Allow-Headers` CORS header. Expects a comma-delimited string, e.g., `Content-Type,Authorization`. If not specified, defaults to reflecting the headers specified in the request’s `Access-Control-Request-Headers` header. | none | -| ENABLE_COLLECTIONS_AUTHX | Enables support for hidden `_collections` query parameter / field when set to `true`. | none (not enabled) | -| ENABLE_THUMBNAILS | Enables support for presigned thumbnails. | none (not enabled) | -| ENABLE_INGEST_ACTION_TRUNCATE | Enables support for ingest action "truncate". | none (not enabled) | +| ENABLE_COLLECTIONS_AUTHX | Enables support for hidden `_collections` query parameter / field when set to `true`. | none (not enabled) | +| ENABLE_THUMBNAILS | Enables support for presigned thumbnails. | none (not enabled) | +| ENABLE_INGEST_ACTION_TRUNCATE | Enables support for ingest action "truncate". | none (not enabled) | +| ENABLE_RESPONSE_COMPRESSION | Enables response compression. Set to 'false' to disable. | enabled | Additionally, the credential for OpenSearch must be configured, as decribed in the section [Populating and accessing credentials](#populating-and-accessing-credentials). @@ -1293,6 +1294,10 @@ The post-hook Lambda configuration may reference any Lambda, not only one deploy of this stack. There is an example post-hook Lambda that can be included with this stack, which provides an example of how to interact with the response, but does not modify it. +If compression is enabled with `ENABLE_RESPONSE_COMPRESSION`, you should ensure that the +post-hook deployed handles compressed responses, or for the example post-hook lambda, +disable compression. + To enable this example post-hook: - Modify bin/build.sh to not exclude the "post-hook" package from being built. diff --git a/bin/system-tests.sh b/bin/system-tests.sh index 49c9c571..8534dc00 100755 --- a/bin/system-tests.sh +++ b/bin/system-tests.sh @@ -8,6 +8,7 @@ export AWS_ACCESS_KEY_ID='none' export AWS_SECRET_ACCESS_KEY='none' export ENABLE_TRANSACTIONS_EXTENSION=true export REQUEST_LOGGING_ENABLED=false +# export ENABLE_RESPONSE_COMPRESSION=false echo "Running tests" set +e diff --git a/package-lock.json b/package-lock.json index 30d8e39e..3e6d560a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,6 +18,7 @@ "@mapbox/extent": "^0.4.0", "@opensearch-project/opensearch": "^2.13.0", "@redocly/cli": "^1.34.2", + "compression": "^1.8.0", "cors": "^2.8.5", "express": "^4.21.2", "got": "^13.0", @@ -25339,6 +25340,60 @@ "node": ">= 10" } }, + "node_modules/compressible": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", + "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", + "license": "MIT", + "dependencies": { + "mime-db": ">= 1.43.0 < 2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/compression": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.8.0.tgz", + "integrity": "sha512-k6WLKfunuqCYD3t6AsuPGvQWaKwuLLh2/xHNcX4qE+vIfDNXpSqnrhwA7O53R7WVQUnt8dVAIW+YHr7xTgOgGA==", + "license": "MIT", + "dependencies": { + "bytes": "3.1.2", + "compressible": "~2.0.18", + "debug": "2.6.9", + "negotiator": "~0.6.4", + "on-headers": "~1.0.2", + "safe-buffer": "5.2.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/compression/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/compression/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/compression/node_modules/negotiator": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.4.tgz", + "integrity": "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", diff --git a/package.json b/package.json index f9fe21b4..d20b7151 100644 --- a/package.json +++ b/package.json @@ -59,6 +59,7 @@ "@mapbox/extent": "^0.4.0", "@opensearch-project/opensearch": "^2.13.0", "@redocly/cli": "^1.34.2", + "compression": "^1.8.0", "cors": "^2.8.5", "express": "^4.21.2", "got": "^13.0", diff --git a/src/lambdas/api/app.js b/src/lambdas/api/app.js index 85391f35..8072a6a3 100644 --- a/src/lambdas/api/app.js +++ b/src/lambdas/api/app.js @@ -1,6 +1,7 @@ import cors from 'cors' import createError from 'http-errors' import express from 'express' +import compression from 'compression' import morgan from 'morgan' import path from 'path' import { fileURLToPath } from 'url' @@ -47,6 +48,10 @@ app.use(cors({ app.use(express.json({ limit: '1mb' })) +if (process.env['ENABLE_RESPONSE_COMPRESSION'] !== 'false') { + app.use(compression()) +} + app.use(addEndpoint) app.get('/', async (req, res, next) => { diff --git a/tests/system/test-api-get-root.js b/tests/system/test-api-get-root.js index ad42022b..cf97a869 100644 --- a/tests/system/test-api-get-root.js +++ b/tests/system/test-api-get-root.js @@ -99,3 +99,13 @@ test('GET / returns links with the correct endpoint if `X-STAC-Endpoint` is set' t.not(apiLink, undefined) t.is(apiLink.href, `${url}/api`) }) + +test('GET / returns a compressed response if ENABLE_RESPONSE_COMPRESSION', async (t) => { + const response = await t.context.api.client.get('', { resolveBodyOnly: false }) + + if (process.env['ENABLE_RESPONSE_COMPRESSION'] !== 'false') { + t.is(response.headers['content-encoding'], 'br') + } else { + t.true(response.headers['content-encoding'] === undefined) + } +})