86 lines
3.3 KiB
JavaScript
Executable file
86 lines
3.3 KiB
JavaScript
Executable file
"use strict";
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
exports.responseInterceptor = void 0;
|
|
const zlib = require("zlib");
|
|
/**
|
|
* Intercept responses from upstream.
|
|
* Automatically decompress (deflate, gzip, brotli).
|
|
* Give developer the opportunity to modify intercepted Buffer and http.ServerResponse
|
|
*
|
|
* NOTE: must set options.selfHandleResponse=true (prevent automatic call of res.end())
|
|
*/
|
|
function responseInterceptor(interceptor) {
|
|
return async function proxyRes(proxyRes, req, res) {
|
|
const originalProxyRes = proxyRes;
|
|
let buffer = Buffer.from('', 'utf8');
|
|
// decompress proxy response
|
|
const _proxyRes = decompress(proxyRes, proxyRes.headers['content-encoding']);
|
|
// concat data stream
|
|
_proxyRes.on('data', (chunk) => (buffer = Buffer.concat([buffer, chunk])));
|
|
_proxyRes.on('end', async () => {
|
|
// copy original headers
|
|
copyHeaders(proxyRes, res);
|
|
// call interceptor with intercepted response (buffer)
|
|
const interceptedBuffer = Buffer.from(await interceptor(buffer, originalProxyRes, req, res));
|
|
// set correct content-length (with double byte character support)
|
|
res.setHeader('content-length', Buffer.byteLength(interceptedBuffer, 'utf8'));
|
|
res.write(interceptedBuffer);
|
|
res.end();
|
|
});
|
|
_proxyRes.on('error', (error) => {
|
|
res.end(`Error fetching proxied request: ${error.message}`);
|
|
});
|
|
};
|
|
}
|
|
exports.responseInterceptor = responseInterceptor;
|
|
/**
|
|
* Streaming decompression of proxy response
|
|
* source: https://github.com/apache/superset/blob/9773aba522e957ed9423045ca153219638a85d2f/superset-frontend/webpack.proxy-config.js#L116
|
|
*/
|
|
function decompress(proxyRes, contentEncoding) {
|
|
let _proxyRes = proxyRes;
|
|
let decompress;
|
|
switch (contentEncoding) {
|
|
case 'gzip':
|
|
decompress = zlib.createGunzip();
|
|
break;
|
|
case 'br':
|
|
decompress = zlib.createBrotliDecompress();
|
|
break;
|
|
case 'deflate':
|
|
decompress = zlib.createInflate();
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
if (decompress) {
|
|
_proxyRes.pipe(decompress);
|
|
_proxyRes = decompress;
|
|
}
|
|
return _proxyRes;
|
|
}
|
|
/**
|
|
* Copy original headers
|
|
* https://github.com/apache/superset/blob/9773aba522e957ed9423045ca153219638a85d2f/superset-frontend/webpack.proxy-config.js#L78
|
|
*/
|
|
function copyHeaders(originalResponse, response) {
|
|
response.statusCode = originalResponse.statusCode;
|
|
response.statusMessage = originalResponse.statusMessage;
|
|
if (response.setHeader) {
|
|
let keys = Object.keys(originalResponse.headers);
|
|
// ignore chunked, brotli, gzip, deflate headers
|
|
keys = keys.filter((key) => !['content-encoding', 'transfer-encoding'].includes(key));
|
|
keys.forEach((key) => {
|
|
let value = originalResponse.headers[key];
|
|
if (key === 'set-cookie') {
|
|
// remove cookie domain
|
|
value = Array.isArray(value) ? value : [value];
|
|
value = value.map((x) => x.replace(/Domain=[^;]+?/i, ''));
|
|
}
|
|
response.setHeader(key, value);
|
|
});
|
|
}
|
|
else {
|
|
response.headers = originalResponse.headers;
|
|
}
|
|
}
|