Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 76 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

21 changes: 16 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,21 @@
"jest": {
"preset": "ts-jest",
"testEnvironment": "node",
"roots": ["<rootDir>/tests"],
"moduleFileExtensions": ["ts", "js", "json"],
"roots": [
"<rootDir>/tests"
],
"moduleFileExtensions": [
"ts",
"js",
"json"
],
"transform": {
"^.+\\.ts$": ["ts-jest", {
"tsconfig": "tsconfig.json"
}]
"^.+\\.ts$": [
"ts-jest",
{
"tsconfig": "tsconfig.json"
}
]
}
},
"prisma": {
Expand All @@ -52,6 +61,7 @@
"@prisma/client": "^5.22.0",
"@stellar/stellar-sdk": "^14.5.0",
"bcryptjs": "^3.0.3",
"compression": "^1.8.1",
"cors": "^2.8.6",
"dotenv": "^17.3.1",
"express": "^5.2.1",
Expand All @@ -66,6 +76,7 @@
},
"devDependencies": {
"@types/bcryptjs": "^2.4.6",
"@types/compression": "^1.8.1",
"@types/cors": "^2.8.19",
"@types/express": "^5.0.6",
"@types/jest": "^30.0.0",
Expand Down
25 changes: 25 additions & 0 deletions scripts/smoke-health.sh
Original file line number Diff line number Diff line change
Expand Up @@ -51,4 +51,29 @@ done
body="$(curl -sf "${BASE_URL}${HEALTH_PATH}")"
echo "[smoke] ${HEALTH_PATH} → 200"
echo "[smoke] Response: ${body}"

# ── Compression middleware verification ────────────────────────────────────────
# Verify the compression middleware is active by checking:
# 1. Vary: Accept-Encoding header (always set by middleware regardless of body size)
# 2. Content-Encoding header (only present when body exceeds 1 KB threshold)
#
# See issue #218.

echo "[smoke] Verifying compression middleware (Vary: Accept-Encoding)..."
vary_header="$(curl -sf -I "${BASE_URL}/health/live" | grep -i '^Vary:' | tr -d '[:space:]')"
if echo "${vary_header}" | grep -qi 'accept-encoding'; then
echo "[smoke] ✓ Vary: Accept-Encoding confirmed — compression middleware active"
else
echo "[smoke] ⚠ Vary: Accept-Encoding not found — compression may not be active"
fi

echo "[smoke] Checking Content-Encoding on large-response endpoints..."
# Use a query parameter or path that generates a larger response to test actual compression
encoding_header="$(curl -sf -H 'Accept-Encoding: gzip' -o /dev/null -w '%{content_encoding}' "${BASE_URL}/health/ready" 2>/dev/null || echo '')"
if [[ -n "${encoding_header}" && "${encoding_header}" != "identity" ]]; then
echo "[smoke] ✓ Content-Encoding: ${encoding_header}"
else
echo "[smoke] (content below 1 KB threshold — compression not expected)"
fi

echo "[smoke] ✓ Production startup smoke check passed"
6 changes: 6 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import adminRouter from './routes/admin'
import metricsRouter from './routes/metrics'
import stellarRouter from './routes/stellar'
import { corsMiddleware, jsonBodyParser, payloadSizeErrorHandler, urlencodedBodyParser } from './middleware/corsandbody'
import compressionMiddleware from './middleware/compression'

// ── Readiness state ───────────────────────────────────────────────────────────
//
Expand Down Expand Up @@ -79,6 +80,11 @@ app.use(requestLogger)
app.use(trustedIpBypass)
app.use(rateLimiter)

// ── Response compression (brotli/gzip) ────────────────────────────────────────
// Compresses responses > 1 KB. Excludes /metrics (Prometheus scraper format).
// Must be before route handlers but after security/parsing/rate-limit middleware.
app.use(compressionMiddleware)

// ── Readiness / liveness probes ───────────────────────────────────────────────
//
// Liveness — is the process running? Always 200 once the process is up.
Expand Down
37 changes: 37 additions & 0 deletions src/middleware/compression.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import compression from 'compression'
import type { Request, Response } from 'express'

/**
* Compression middleware configuration.
*
* Features:
* - Brotli (br) preferred when the client advertises support (Accept-Encoding: br),
* falling back to gzip, then deflate.
* - Threshold: only compress responses larger than 1 KB.
* - Excludes the /metrics endpoint (Prometheus scraper handles its own format).
*
* The compression package (v1.7+) uses Node's native zlib.createBrotliCompress when
* available, so no additional dependencies are required for brotli support.
*/

const EXCLUDED_PATHS = ['/metrics']

function compressionFilter(req: Request, res: Response): boolean {
// Never compress the Prometheus metrics endpoint
if (EXCLUDED_PATHS.includes(req.path)) {
return false
}

// Fall back to the default filter (checks Content-Type is compressible)
return compression.filter(req, res)
}

const compressionMiddleware = compression({
// Compress responses larger than 1 KB
threshold: 1024,

// Custom filter to exclude /metrics
filter: compressionFilter,
})

export default compressionMiddleware