Skip to content
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

Proxyman interferes with WebSocket upgrade request & causes race condition #2283

Open
liambutler-lawrence opened this issue Mar 19, 2025 · 4 comments
Assignees
Labels
bug Something isn't working

Comments

@liambutler-lawrence
Copy link

Description

When using Proxyman to trace a WS connection with SSL Proxying enabled, Proxyman sends an HTTP 101 response back to the client immediately (within 5ms), before the server has actually returned its HTTP response. This causes a race condition in clients that need to wait for the upgrade (101) response before doing additional logic.

Steps to Reproduce

  1. Start Proxyman and enable SSL Proxying for a WS API
  2. Using any local client, open a WS connection at that URL

Current Behavior

Proxyman receives the HTTP upgrade request from the client and forwards it to the server, but does not wait for the server response before responding to the client. Instead, it immediately sends a fake success (HTTP 101) response to the client:

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Sec-WebSocket-Accept: pIF19n7SVsW0u4/t7tCDGOzUNnY=
Connection: upgrade

Since the Sec-WebSocket-Accept header is correctly computed, this causes the client to erroneously believe the upgrade process has already completed. If the client waits for the upgrade process to complete before doing additional logic, this causes a race condition and all sorts of bad behavior.

Expected Behavior

Proxyman should wait for the HTTP response (whether 101 or not) and forward that response to the client, just like the behavior for other HTTP requests.

Environment

  • App version: 5.16.0
  • macOS version: 15.3.2 (24D81)
@liambutler-lawrence liambutler-lawrence added the bug Something isn't working label Mar 19, 2025
@NghiaTranUIT NghiaTranUIT self-assigned this Mar 20, 2025
@NghiaTranUIT
Copy link
Member

@liambutler-lawrence we're aware of it. Sorry I tried to fix it because it's how we currently design the Websocket Mitm proxy. It will return 101 status code first, then make a real connection to a WS socket server. Then it exchanges the data.

If the client waits for the upgrade process to complete before doing additional logic, this causes a race condition and all sorts of bad behavior.

Not sure why it causes the race condition? Does your WS Server return any new Headers that your client needs to read?

From what I know 99% WS client just needs the Sec-WebSocket-Accept header to start exchange data

@liambutler-lawrence
Copy link
Author

liambutler-lawrence commented Mar 21, 2025

Our WS server is stateful- during the connection process it saves the WS connection ID to a DB. On the client side, as soon as it receives the 101 response, it assumes the DB has been updated and begins making other REST calls to our backend, which fail if the DB has actually not been updated yet. Hence the race condition.

More generally though, this behavior simply violates the basic expectation of a proxy—that it should inspect and log inbound/outbound traffic, but not interfere with it. I don't understand why your WS implementation is special: until the upgrade handshake is completed, it's just a normal HTTP flow.

@NghiaTranUIT
Copy link
Member

thanks, we understand. The reason is we're using SwiftNIO with a built-in websocket upgrade for server/client.

We will refactor it by manually handling the upgrader. It will fix your problem 👍

@liambutler-lawrence
Copy link
Author

Makes sense, thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

2 participants