Skip to content

fix(websocket): handle errors in handleUpgrade #823

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Apr 3, 2025

Conversation

nwalters512
Copy link
Contributor

@nwalters512 nwalters512 commented Sep 5, 2022

Description

When there is an error from user-provided code (pathFilter, rewritePath, or router), that error is now handled and exposed to the user via proxy.emit('error', ...).

error-response-plugin was also updated to handle the fact that it might receive a socket. If it does, it will call socket.destroy().

Motivation and Context

See #822 for a description of the issue being fixed. This PR closes #822.

How has this been tested?

I added a new test case. It fails on master, and works correctly with my new changes.

Types of changes

I'm split on whether or not this should be considered a breaking change. Folks could have written code assuming that the error handler would always get a Response as the third argument, whereas now it would get a Socket. That said, the TypeScript types from http-proxy do reflect that that could be a Socket, so it shouldn't come as too much of a surprise.

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)

Checklist:

  • My change requires a change to the documentation.
  • I have updated the documentation accordingly.

Comment on lines +9 to +11
function isSocketLike(obj: any): obj is Socket {
return obj && typeof obj.write === 'function' && !('writeHead' in obj);
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can't just check for instanceof Socket since per the documentation, the user may be able to provide a different socket implementation:

This event is guaranteed to be passed an instance of the <net.Socket> class, a subclass of <stream.Duplex>, unless the user specifies a socket type other than <net.Socket>.

@@ -97,15 +97,14 @@ describe('E2E WebSocket proxy', () => {

describe('with router and pathRewrite', () => {
beforeEach(() => {
// override
proxyServer = createApp(
const proxyMiddleware = createProxyMiddleware({
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was a bug with the existing tests: because it didn't reassign proxyMiddleware, the proxyMiddleware.upgrade access a few lines after this was reading from the wrong object.

@nwalters512
Copy link
Contributor Author

@chimurai 👋 can I do anything to help this along?

@chimurai
Copy link
Owner

Sorry for the late reply. I'll need some time to review 🙏

@nwalters512
Copy link
Contributor Author

@chimurai thanks!

@nwalters512
Copy link
Contributor Author

@chimurai is there any chance you'd be able to review soon? If so, I'll resolve the merge conflicts with master.

@nwalters512
Copy link
Contributor Author

@chimurai sorry to poke again, but I'd like to understand if there's a chance that this will be merged, or if I'll have to fork. Thanks for your work maintaining this package!

Copy link

pkg-pr-new bot commented Mar 23, 2025

npm i https://pkg.pr.new/http-proxy-middleware@823

commit: ef93655

@coveralls
Copy link

coveralls commented Mar 23, 2025

Coverage Status

coverage: 97.33% (+0.05%) from 97.284%
when pulling ef93655 on nwalters512:handle-websocket-errors
into e94087e on chimurai:master.

@chimurai
Copy link
Owner

chimurai commented Mar 23, 2025

Thanks for the PR and patience @nwalters512

PR looks good 🙏

Could you verify if your fix works with the package preview from pkg-pr-new?

Once verified, I'll merge and publish a new version

@chimurai chimurai requested a review from Copilot March 30, 2025 14:03
Copy link

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR addresses issues with error handling during WebSocket upgrades by catching errors arising from user-provided router or path rewriting functions and propagating them through the proxy’s error event.

  • Introduces a try/catch block in the WebSocket upgrade handler to catch errors from prepareProxyRequest.
  • Updates the error-response-plugin to distinguish between HTTP response and socket objects for error handling.
  • Adds a new test case to ensure that errors in the router are properly handled by the proxy.

Reviewed Changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated no comments.

File Description
test/e2e/websocket.spec.ts Adds a test validating that an error thrown in the router is handled correctly.
src/plugins/default/error-response-plugin.ts Refactors error handling to differentiate between HTTP responses and sockets.
src/http-proxy-middleware.ts Wraps the WebSocket upgrade handling in a try/catch to emit errors appropriately.
Comments suppressed due to low confidence (3)

test/e2e/websocket.spec.ts:145

  • [nitpick] Consider asserting specific properties of the error (e.g., the error message) to ensure that the error handling path in handleUpgrade is fully validated.
ws.on('error', (err) => {

src/plugins/default/error-response-plugin.ts:30

  • Confirm that destroying the socket here is the intended behavior and consider adding a comment or logging to clarify why a socket is destroyed rather than sending an error response.
res.destroy();

src/http-proxy-middleware.ts:107

  • [nitpick] Consider logging the caught error within the catch block to aid in debugging before emitting it via this.proxy.emit.
catch (err) {

@nwalters512
Copy link
Contributor Author

@chimurai unfortunately I haven't been able to deal with the breaking changes from v3 yet, so I'm unable to test this in the context of my application. Now that I know this fix is coming, that's probably enough motivation to get me to upgrade 🙂 let me try to do that, and I'll test this if I succeed.

@chimurai
Copy link
Owner

chimurai commented Apr 1, 2025

Hope the following guides are helpful to upgrade:

https://github.com/chimurai/http-proxy-middleware/blob/master/MIGRATION.md
V3 Discussions: #768

Let me know if you bump into issues 🙏

@nwalters512
Copy link
Contributor Author

nwalters512 commented Apr 2, 2025

@chimurai I got myself upgraded and tried out this PR, it's working exactly how I want it to 🙏 I added some artificial errors to the bits of my code that deal with websocket routing and confirmed that they're now passed to the error handler I configured.

@chimurai chimurai merged commit fd0f568 into chimurai:master Apr 3, 2025
12 checks passed
@chimurai
Copy link
Owner

chimurai commented Apr 3, 2025

Thanks @nwalters512 for contribution 🙏

@nwalters512 nwalters512 deleted the handle-websocket-errors branch April 3, 2025 20:22
@nwalters512
Copy link
Contributor Author

@chimurai for my own planning, do you have a plan for when you'll cut the next release with this change?

@chimurai
Copy link
Owner

chimurai commented Apr 7, 2025

Hi @nwalters512. I hope to create a release somewhere this week.

@chimurai
Copy link
Owner

chimurai commented Apr 9, 2025

@nwalters512 just released https://github.com/chimurai/http-proxy-middleware/releases/tag/v3.0.4

thanks again for your contribution 🙏

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Errors in prepareProxyRequest not handled for websockets
3 participants