debug-proxy-errors-plugin.js 3.6 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677
  1. import { styleText } from 'node:util';
  2. import { Debug } from '../../debug.js';
  3. import { definePlugin } from '../define-plugin.js';
  4. const debug = Debug.extend('debug-proxy-errors-plugin');
  5. const BODY_PARSER_ERROR_MESSAGE = `[HPM] Connection reset (ECONNRESET) detected with non-empty "req.body" [ERR_HPM.GH40].
  6. This usually means that the POST request body (req.body) was already parsed before reaching the proxy.
  7. When bodyParser runs first, it consumes the request stream, leaving the proxy unable to forward the body data to the target server.
  8. How to fix this issue:
  9. - Option 1: Place the proxy middleware before the bodyParser middleware.
  10. - Option 2: Use 'fixRequestBody()' helper to fix this issue.
  11. For more details, see: https://github.com/chimurai/http-proxy-middleware/issues/40\n`;
  12. function hasParsedBody(req) {
  13. return Boolean(req && req.method === 'POST' && 'body' in req && req.body);
  14. }
  15. /**
  16. * Subscribe to {@link https://github.com/unjs/httpxy#events `httpxy` error events} to prevent server from crashing.
  17. * Errors are logged with {@link https://www.npmjs.com/package/debug debug} library.
  18. */
  19. export const debugProxyErrorsPlugin = definePlugin((proxyServer, options) => {
  20. /**
  21. * The old `http-proxy` doesn't handle any errors by default (https://github.com/http-party/node-http-proxy#listening-for-proxy-events)
  22. * > We do not do any error handling of messages passed between client and proxy, and messages passed between proxy and target, so it is recommended that you listen on errors and handle them.
  23. * Subscribing to error event to prevent server from crashing
  24. */
  25. proxyServer.on('error', (error, req, res, target) => {
  26. debug(`httpxy error event: \n%O`, error);
  27. // detect request body (when bodyParser used) and log an error message to help debugging
  28. if (error.code === 'ECONNRESET' && hasParsedBody(req)) {
  29. console.error(styleText('red', BODY_PARSER_ERROR_MESSAGE));
  30. }
  31. });
  32. proxyServer.on('proxyReq', (proxyReq, req, socket) => {
  33. socket.on('error', (error) => {
  34. debug('Socket error in proxyReq event: \n%O', error);
  35. });
  36. });
  37. /**
  38. * Fix SSE close events
  39. * @link https://github.com/chimurai/http-proxy-middleware/issues/678
  40. * @link https://github.com/http-party/node-http-proxy/issues/1520#issue-877626125
  41. */
  42. proxyServer.on('proxyRes', (proxyRes, req, res) => {
  43. res.on('close', () => {
  44. if (!res.writableEnded) {
  45. debug('Destroying proxyRes in proxyRes close event');
  46. proxyRes.destroy();
  47. }
  48. });
  49. });
  50. /**
  51. * Fix crash when target server restarts
  52. * https://github.com/chimurai/http-proxy-middleware/issues/476#issuecomment-746329030
  53. * https://github.com/webpack/webpack-dev-server/issues/1642#issuecomment-790602225
  54. */
  55. proxyServer.on('proxyReqWs', (proxyReq, req, socket) => {
  56. socket.on('error', (error) => {
  57. debug('Socket error in proxyReqWs event: \n%O', error);
  58. });
  59. });
  60. proxyServer.on('open', (proxySocket) => {
  61. proxySocket.on('error', (error) => {
  62. debug('Socket error in open event: \n%O', error);
  63. });
  64. });
  65. proxyServer.on('close', (req, socket, head) => {
  66. socket.on('error', (error) => {
  67. debug('Socket error in close event: \n%O', error);
  68. });
  69. });
  70. // https://github.com/webpack/webpack-dev-server/issues/1642#issuecomment-1103136590
  71. proxyServer.on('econnreset', (error, req, res, target) => {
  72. debug(`httpxy econnreset event: \n%O`, error);
  73. });
  74. });