fix-request-body.js 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778
  1. import * as querystring from 'node:querystring';
  2. import * as zlib from 'node:zlib';
  3. import { stringifyFormData } from './fix-request-body-utils/stringify-form-data.js';
  4. /**
  5. * Fix proxied body if bodyParser is involved.
  6. *
  7. * @example
  8. * ```ts
  9. * createProxyMiddleware({
  10. * target: 'http://example.com',
  11. * on: {
  12. * proxyReq: fixRequestBody,
  13. * }
  14. * });
  15. * ```
  16. *
  17. * Alternative solution without using `fixRequestBody()`: put `http-proxy-middleware` before `bodyParser` in the middleware stack.
  18. *
  19. * @see {@link https://github.com/chimurai/http-proxy-middleware/issues/40 Github issue #40 - POST request body is not proxied}
  20. */
  21. export function fixRequestBody(proxyReq, req) {
  22. // skip fixRequestBody() when req.readableLength not 0 (bodyParser failure)
  23. if (req.readableLength !== 0) {
  24. return;
  25. }
  26. const requestBody = req.body;
  27. if (!requestBody) {
  28. return;
  29. }
  30. const contentType = proxyReq.getHeader('Content-Type');
  31. if (!contentType) {
  32. return;
  33. }
  34. const writeBody = (bodyData) => {
  35. let proxyData = bodyData;
  36. const contentEncoding = String(proxyReq.getHeader('Content-Encoding')).toLowerCase();
  37. switch (contentEncoding) {
  38. case 'br':
  39. proxyData = zlib.brotliCompressSync(proxyData);
  40. break;
  41. case 'deflate':
  42. proxyData = zlib.deflateSync(proxyData);
  43. break;
  44. case 'gzip':
  45. proxyData = zlib.gzipSync(proxyData);
  46. break;
  47. case 'zstd':
  48. proxyData = zlib.zstdCompressSync(proxyData);
  49. break;
  50. }
  51. proxyReq.setHeader('Content-Length', Buffer.byteLength(proxyData));
  52. proxyReq.write(proxyData);
  53. };
  54. try {
  55. // Use if-elseif to prevent multiple writeBody/setHeader calls:
  56. // Error: "Cannot set headers after they are sent to the client"
  57. if (contentType.includes('application/json') || contentType.includes('+json')) {
  58. writeBody(JSON.stringify(requestBody));
  59. }
  60. else if (contentType.includes('application/x-www-form-urlencoded')) {
  61. writeBody(querystring.stringify(requestBody));
  62. }
  63. else if (contentType.includes('multipart/form-data')) {
  64. writeBody(stringifyFormData(contentType, requestBody));
  65. }
  66. else if (contentType.includes('text/plain')) {
  67. writeBody(requestBody);
  68. }
  69. }
  70. catch (error) {
  71. // proxyReq listeners run outside the middleware try/catch path; re-throwing here can bubble as
  72. // an unhandled exception in consumers, so destroy() is used to fail closed through proxy error handling.
  73. proxyReq.destroy(toError(error));
  74. }
  75. }
  76. function toError(error) {
  77. return error instanceof Error ? error : new Error(String(error));
  78. }