From 99a616717b898271cc075c83e0db976574795079 Mon Sep 17 00:00:00 2001 From: Karol Czeryna Date: Sun, 30 Mar 2025 15:29:14 +0200 Subject: [PATCH] add gzip and brotli support to FetchMode.PROXY mode --- proxy.ts | 73 ++++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 61 insertions(+), 12 deletions(-) diff --git a/proxy.ts b/proxy.ts index 4a25bc633..eea5e979e 100644 --- a/proxy.ts +++ b/proxy.ts @@ -2,6 +2,7 @@ import { FetchMode, ServerSetting } from './src/types/types'; import { Connect } from 'vite'; import httpProxy from 'http-proxy'; import { exec } from 'child_process'; +import { brotliDecompressSync, gunzipSync } from 'zlib'; const proxy = httpProxy.createProxyServer({}); @@ -211,19 +212,67 @@ proxy.on('proxyRes', function (proxyRes, req, res) { proxyRequest(req, res); return false; } - for (const _header in proxyRes.headers) { - if (!settings.disAllowResponseHeaders.includes(_header)) { - res.setHeader(_header, proxyRes.headers[_header] as string); + + const contentEncoding = proxyRes.headers['content-encoding']; + const isBrotli = + contentEncoding && + (Array.isArray(contentEncoding) + ? contentEncoding.some(enc => enc.includes('br')) + : contentEncoding.includes('br')); + + const isGzip = + contentEncoding && + (Array.isArray(contentEncoding) + ? contentEncoding.some(enc => enc.includes('gzip')) + : contentEncoding.includes('gzip')); + + if (isBrotli || isGzip) { + delete proxyRes.headers['content-encoding']; + delete proxyRes.headers['content-length']; + + for (const _header in proxyRes.headers) { + if (!settings.disAllowResponseHeaders.includes(_header)) { + res.setHeader(_header, proxyRes.headers[_header] as string); + } } + + const chunks: Buffer[] = []; + proxyRes.on('data', chunk => chunks.push(Buffer.from(chunk))); + proxyRes.on('end', async function () { + try { + const buffer = Buffer.concat(chunks); + let decompressed; + + if (isBrotli) { + decompressed = brotliDecompressSync(buffer); + } else { + decompressed = gunzipSync(buffer); + } + + res.write(Buffer.from(decompressed)); + res.end(); + } catch (err) { + console.error(err); + res.statusCode = 500; + res.end(`Error decompressing ${isBrotli ? 'Brotli' : 'GZIP'} content`); + } + }); + } else { + for (const _header in proxyRes.headers) { + if (!settings.disAllowResponseHeaders.includes(_header)) { + res.setHeader(_header, proxyRes.headers[_header] as string); + } + } + for (const _header in settings.disAllowedRequestHeaders) { + delete proxyRes.headers[_header]; + } + proxyRes.on('data', function (chunk) { + res.write(chunk); + }); + proxyRes.on('end', function () { + res.end(); + }); } - for (const _header in settings.disAllowedRequestHeaders) { - delete proxyRes.headers[_header]; - } - proxyRes.on('data', function (chunk) { - res.write(chunk); - }); - proxyRes.on('end', function () { - res.end(); - }); }); + export { proxyHandlerMiddle, proxySettingMiddleware };