UNPKG

underpost

Version:

Underpost Platform — end-to-end CI/CD and application-delivery toolchain CLI. Covers bare metal, Kubernetes, K3s, kubeadm, LXD, container/image orchestration, secrets, databases, cron jobs, monitoring, SSH, runners, PWA + Workbox delivery, and release orc

187 lines (179 loc) 7.2 kB
/** * Response compression policy for the Nginx workloads on the request path. * * @module src/server/underpost-compression.js * @namespace UnderpostCompression */ /** * @constant UNDERPOST_COMPRESSION * @description The compression policy both edge workloads render from. * * `text/html` appears in no type list on purpose: nginx and ngx_brotli both * always compress it, and naming it again only invites the two lists to drift. * Every entry is a format that is still text on the wire — already-compressed * media (png, woff2, mp4) is excluded, since re-encoding it spends CPU to add * bytes. * @memberof UnderpostCompression */ const UNDERPOST_COMPRESSION = { types: [ 'application/atom+xml', 'application/geo+json', 'application/javascript', 'application/json', 'application/ld+json', 'application/manifest+json', 'application/rss+xml', 'application/vnd.api+json', 'application/wasm', 'application/x-javascript', 'application/xhtml+xml', 'application/xml', 'font/otf', 'font/ttf', 'image/svg+xml', 'image/x-icon', 'text/css', 'text/javascript', 'text/markdown', 'text/plain', 'text/xml', ], // Below roughly this size a compressed body plus its headers is no smaller // than the original, and can be larger. minLength: 512, // Level 5 of 9: within a few percent of maximum ratio at a fraction of the // CPU. The edge compresses every response of every host, so the cost of a // higher level is paid on the shared workload, not on one deploy. gzipLevel: 5, // Brotli's scale runs to 11, where dynamic compression becomes slower than // the transfer it saves. 5 is the usual on-the-fly ceiling. brotliLevel: 5, filterModule: 'ngx_http_brotli_filter_module.so', staticModule: 'ngx_http_brotli_static_module.so', defaultImage: 'nginx:alpine', env: { image: 'UNDERPOST_NGINX_IMAGE', brotliModules: 'UNDERPOST_NGINX_BROTLI_MODULES', enabled: 'UNDERPOST_NGINX_COMPRESSION', }, }; /** * @method nginxImageFactory * @description The image both edge workloads run. * * Resolved in one place because the two have to agree: they sit on the same * request path, and brotli is a property of the image rather than of the * config. An image carrying ngx_brotli is the only way the brotli directives * below can be rendered at all. * @returns {string} Container image reference. * @memberof UnderpostCompression */ const nginxImageFactory = () => `${process.env[UNDERPOST_COMPRESSION.env.image] || ''}`.trim() || UNDERPOST_COMPRESSION.defaultImage; /** * @method brotliModuleDirFactory * @description Directory holding the brotli dynamic modules, or an empty string * when the image is not declared to carry them. * * Empty is the default and the safe state: gzip alone, on a stock image, with * no `load_module` line that could fail to resolve. * @returns {string} Absolute directory path, or `''`. * @memberof UnderpostCompression */ const brotliModuleDirFactory = () => `${process.env[UNDERPOST_COMPRESSION.env.brotliModules] || ''}`.trim().replace(/\/+$/, ''); /** * @method compressionEnabledFactory * @description Whether responses are compressed at all. * * The escape hatch for a CPU-bound edge node: compression trades cycles for * bytes, and an operator who is out of the former rather than the latter needs * to turn it off without editing generated configuration. * @returns {boolean} True unless explicitly disabled. * @memberof UnderpostCompression */ const compressionEnabledFactory = () => !['off', '0', 'false', 'no'].includes(`${process.env[UNDERPOST_COMPRESSION.env.enabled] || ''}`.trim().toLowerCase()); /** * @method compressionModulesConfFactory * @description The `load_module` lines brotli needs, in nginx's main context. * * Rendered only alongside the brotli directives that require them, so a config * never loads a module it does not use or uses one it did not load — using one * that was not loaded is a start-up failure, and the reverse is surface on a * workload that has no documents to serve from it. * @param {string} [brotliModuleDir] - Directory holding the modules; empty renders nothing. * @param {boolean} [enabled] - Whether compression is on at all. * @param {boolean} [staticRoot] - Whether the workload serves documents from disk. * @returns {string} Main-context directives, or an empty string. * @memberof UnderpostCompression */ const compressionModulesConfFactory = ({ brotliModuleDir = brotliModuleDirFactory(), enabled = compressionEnabledFactory(), staticRoot = false, } = {}) => { if (!enabled || !brotliModuleDir) return ''; return [ `load_module ${brotliModuleDir}/${UNDERPOST_COMPRESSION.filterModule};`, ...(staticRoot ? [`load_module ${brotliModuleDir}/${UNDERPOST_COMPRESSION.staticModule};`] : []), ].join('\n'); }; /** * @method compressionConfFactory * @description The compression block for an nginx `http` context. * * `gzip_proxied any` is what makes this worth rendering on a reverse proxy at * all: the default is `off`, which suppresses compression for exactly the * responses these workloads exist to forward. A response the upstream already * encoded is passed through untouched either way — nginx never re-compresses * one that carries `Content-Encoding`. * * `*_static` is only rendered where a document root exists. It costs one stat() * per request to serve an operator-placed `.gz`/`.br` sibling with no CPU at * all, and has nothing to look for on a workload that serves no files. * @param {boolean} [enabled] - Whether compression is on at all. * @param {string} [brotliModuleDir] - Directory holding the brotli modules; empty renders gzip alone. * @param {boolean} [staticRoot] - Whether the workload serves documents from disk. * @param {string} [indent] - Leading whitespace for each line. * @returns {string} `http`-context directives, or an empty string. * @memberof UnderpostCompression */ const compressionConfFactory = ({ enabled = compressionEnabledFactory(), brotliModuleDir = brotliModuleDirFactory(), staticRoot = false, indent = ' ', } = {}) => { if (!enabled) return ''; const types = UNDERPOST_COMPRESSION.types.join(' '); const lines = [ 'gzip on;', // Compressed and uncompressed bodies share a URL, so a cache that ignores // this header serves one to a client that asked for the other. 'gzip_vary on;', `gzip_comp_level ${UNDERPOST_COMPRESSION.gzipLevel};`, `gzip_min_length ${UNDERPOST_COMPRESSION.minLength};`, 'gzip_proxied any;', `gzip_types ${types};`, ...(staticRoot ? ['gzip_static on;'] : []), ]; if (brotliModuleDir) lines.push( 'brotli on;', `brotli_comp_level ${UNDERPOST_COMPRESSION.brotliLevel};`, `brotli_min_length ${UNDERPOST_COMPRESSION.minLength};`, `brotli_types ${types};`, ...(staticRoot ? ['brotli_static on;'] : []), ); return lines.map((line) => `${indent}${line}`).join('\n'); }; export { UNDERPOST_COMPRESSION, brotliModuleDirFactory, compressionConfFactory, compressionEnabledFactory, compressionModulesConfFactory, nginxImageFactory, };