response-interceptor.js 4.1 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697
  1. "use strict";
  2. var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
  3. function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
  4. return new (P || (P = Promise))(function (resolve, reject) {
  5. function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
  6. function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
  7. function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
  8. step((generator = generator.apply(thisArg, _arguments || [])).next());
  9. });
  10. };
  11. Object.defineProperty(exports, "__esModule", { value: true });
  12. exports.responseInterceptor = void 0;
  13. const zlib = require("zlib");
  14. /**
  15. * Intercept responses from upstream.
  16. * Automatically decompress (deflate, gzip, brotli).
  17. * Give developer the opportunity to modify intercepted Buffer and http.ServerResponse
  18. *
  19. * NOTE: must set options.selfHandleResponse=true (prevent automatic call of res.end())
  20. */
  21. function responseInterceptor(interceptor) {
  22. return function proxyRes(proxyRes, req, res) {
  23. return __awaiter(this, void 0, void 0, function* () {
  24. const originalProxyRes = proxyRes;
  25. let buffer = Buffer.from('', 'utf8');
  26. // decompress proxy response
  27. const _proxyRes = decompress(proxyRes, proxyRes.headers['content-encoding']);
  28. // concat data stream
  29. _proxyRes.on('data', (chunk) => (buffer = Buffer.concat([buffer, chunk])));
  30. _proxyRes.on('end', () => __awaiter(this, void 0, void 0, function* () {
  31. // copy original headers
  32. copyHeaders(proxyRes, res);
  33. // call interceptor with intercepted response (buffer)
  34. const interceptedBuffer = Buffer.from(yield interceptor(buffer, originalProxyRes, req, res));
  35. // set correct content-length (with double byte character support)
  36. res.setHeader('content-length', Buffer.byteLength(interceptedBuffer, 'utf8'));
  37. res.write(interceptedBuffer);
  38. res.end();
  39. }));
  40. _proxyRes.on('error', (error) => {
  41. res.end(`Error fetching proxied request: ${error.message}`);
  42. });
  43. });
  44. };
  45. }
  46. exports.responseInterceptor = responseInterceptor;
  47. /**
  48. * Streaming decompression of proxy response
  49. * source: https://github.com/apache/superset/blob/9773aba522e957ed9423045ca153219638a85d2f/superset-frontend/webpack.proxy-config.js#L116
  50. */
  51. function decompress(proxyRes, contentEncoding) {
  52. let _proxyRes = proxyRes;
  53. let decompress;
  54. switch (contentEncoding) {
  55. case 'gzip':
  56. decompress = zlib.createGunzip();
  57. break;
  58. case 'br':
  59. decompress = zlib.createBrotliDecompress();
  60. break;
  61. case 'deflate':
  62. decompress = zlib.createInflate();
  63. break;
  64. default:
  65. break;
  66. }
  67. if (decompress) {
  68. _proxyRes.pipe(decompress);
  69. _proxyRes = decompress;
  70. }
  71. return _proxyRes;
  72. }
  73. /**
  74. * Copy original headers
  75. * https://github.com/apache/superset/blob/9773aba522e957ed9423045ca153219638a85d2f/superset-frontend/webpack.proxy-config.js#L78
  76. */
  77. function copyHeaders(originalResponse, response) {
  78. response.statusCode = originalResponse.statusCode;
  79. response.statusMessage = originalResponse.statusMessage;
  80. if (response.setHeader) {
  81. let keys = Object.keys(originalResponse.headers);
  82. // ignore chunked, brotli, gzip, deflate headers
  83. keys = keys.filter((key) => !['content-encoding', 'transfer-encoding'].includes(key));
  84. keys.forEach((key) => {
  85. let value = originalResponse.headers[key];
  86. if (key === 'set-cookie') {
  87. // remove cookie domain
  88. value = Array.isArray(value) ? value : [value];
  89. value = value.map((x) => x.replace(/Domain=[^;]+?/i, ''));
  90. }
  91. response.setHeader(key, value);
  92. });
  93. }
  94. else {
  95. response.headers = originalResponse.headers;
  96. }
  97. }