diff --git a/packages/core/src/server/getDevMiddlewares.ts b/packages/core/src/server/getDevMiddlewares.ts index 7130d28715..604dc23f25 100644 --- a/packages/core/src/server/getDevMiddlewares.ts +++ b/packages/core/src/server/getDevMiddlewares.ts @@ -83,7 +83,7 @@ const applyDefaultMiddlewares = async ({ // compression should be the first middleware if (server.compress) { const { gzipMiddleware } = await import('./gzipMiddleware'); - middlewares.push(gzipMiddleware); + middlewares.push(gzipMiddleware()); } middlewares.push((req, res, next) => { diff --git a/packages/core/src/server/gzipMiddleware.ts b/packages/core/src/server/gzipMiddleware.ts index 158158465a..e5f8520569 100644 --- a/packages/core/src/server/gzipMiddleware.ts +++ b/packages/core/src/server/gzipMiddleware.ts @@ -20,95 +20,103 @@ const shouldCompress = (res: ServerResponse) => { return size === undefined || Number(size) > 1024; }; -export const gzipMiddleware: RequestHandler = (req, res, next): void => { - const accept = req.headers['accept-encoding']; - const encoding = typeof accept === 'string' && ENCODING_REGEX.test(accept); - - if (req.method === 'HEAD' || !encoding) { - next(); - return; - } - - let gzip: zlib.Gzip | undefined; - let writeHeadStatus: number | undefined; - let started = false; - - const { end, write, on, writeHead } = res; - const listeners: Array<[string | symbol, (...args: any[]) => void]> = []; - - const start = () => { - if (started) { +export const gzipMiddleware = + ({ + level = zlib.constants.Z_BEST_SPEED, + }: { + level?: number; + } = {}): RequestHandler => + (req, res, next): void => { + const accept = req.headers['accept-encoding']; + const encoding = typeof accept === 'string' && ENCODING_REGEX.test(accept); + + if (req.method === 'HEAD' || !encoding) { + next(); return; } - started = true; - if (shouldCompress(res)) { - res.setHeader('Content-Encoding', 'gzip'); - res.removeHeader('Content-Length'); + let gzip: zlib.Gzip | undefined; + let writeHeadStatus: number | undefined; + let started = false; - gzip = zlib.createGzip({ level: zlib.constants.Z_BEST_SPEED }); + const { end, write, on, writeHead } = res; + const listeners: Array<[string | symbol, (...args: any[]) => void]> = []; - gzip.on('data', (chunk) => { - if ((write as (chunk: unknown) => boolean).call(res, chunk) === false) { - gzip!.pause(); - } - }); + const start = () => { + if (started) { + return; + } + started = true; - on.call(res, 'drain', () => gzip!.resume()); + if (shouldCompress(res)) { + res.setHeader('Content-Encoding', 'gzip'); + res.removeHeader('Content-Length'); - gzip.on('end', () => { - (end as () => void).call(res); - }); + gzip = zlib.createGzip({ level }); - for (const listener of listeners) { - gzip.on.apply(gzip, listener); - } - } else { - for (const listener of listeners) { - on.apply(res, listener); - } - } + gzip.on('data', (chunk) => { + if ( + (write as (chunk: unknown) => boolean).call(res, chunk) === false + ) { + gzip!.pause(); + } + }); - writeHead.call(res, writeHeadStatus ?? res.statusCode); - }; + on.call(res, 'drain', () => gzip!.resume()); - res.writeHead = (status, reason, headers?) => { - if (reason) { - for (const [key, value] of Object.entries(headers || reason)) { - res.setHeader(key, value); - } - } - writeHeadStatus = status; - return res; - }; + gzip.on('end', () => { + (end as () => void).call(res); + }); - res.write = (...args: unknown[]) => { - start(); - return gzip - ? gzip.write(...(args as Parameters)) - : write.apply(res, args as Parameters); - }; + for (const listener of listeners) { + gzip.on.apply(gzip, listener); + } + } else { + for (const listener of listeners) { + on.apply(res, listener); + } + } - res.end = (...args: any[]) => { - start(); - return gzip - ? (gzip.end as unknown as typeof end)(...args) - : end.apply(res, args as Parameters); - }; + writeHead.call(res, writeHeadStatus ?? res.statusCode); + }; - res.on = (type, listener) => { - if (started) { - if (!gzip || type !== 'drain') { - on.call(res, type, listener); + res.writeHead = (status, reason, headers?) => { + if (reason) { + for (const [key, value] of Object.entries(headers || reason)) { + res.setHeader(key, value); + } + } + writeHeadStatus = status; + return res; + }; + + res.write = (...args: unknown[]) => { + start(); + return gzip + ? gzip.write(...(args as Parameters)) + : write.apply(res, args as Parameters); + }; + + res.end = (...args: any[]) => { + start(); + return gzip + ? (gzip.end as unknown as typeof end)(...args) + : end.apply(res, args as Parameters); + }; + + res.on = (type, listener) => { + if (started) { + if (!gzip || type !== 'drain') { + on.call(res, type, listener); + } else { + gzip.on(type, listener); + } } else { - gzip.on(type, listener); + // store listeners until start + listeners.push([type, listener]); } - } else { - // store listeners until start - listeners.push([type, listener]); - } - return res; - }; + return res; + }; - next(); -}; + next(); + }; diff --git a/packages/core/src/server/prodServer.ts b/packages/core/src/server/prodServer.ts index 84c2407c97..aa5ee3bac7 100644 --- a/packages/core/src/server/prodServer.ts +++ b/packages/core/src/server/prodServer.ts @@ -61,7 +61,12 @@ export class RsbuildProdServer { // compression should be the first middleware if (compress) { const { gzipMiddleware } = await import('./gzipMiddleware'); - this.middlewares.use(gzipMiddleware); + this.middlewares.use( + gzipMiddleware({ + // simulates the common gzip compression rates + level: 6, + }), + ); } if (headers) {