[GH-ISSUE #1055] [Bug] Proxyman breaks web sockets when websocket subprotocols are specified #1049

Open
opened 2026-03-03 19:24:06 +03:00 by kerem · 11 comments
Owner

Originally created by @Bertrand on GitHub (Nov 2, 2021).
Original GitHub issue: https://github.com/ProxymanApp/Proxyman/issues/1055

Originally assigned to: @NghiaTranUIT on GitHub.

Proxyman version

23410

macOS Version

11.5.2

Description

We're using a websocket client for graphql subscriptions from a web client.
When Proxyman is started, the web browser is unable to connect to the websocket server.

This is because the graphql subscription library that we use passes a suprotocol at connect time (namely "graphql-ws").
We've been able to isolate the issue in a very simple node server:

proxyman-ws-sample.zip

Steps to reproduce

  • unzip the server
  • npm install
  • node ./server.js

Then

Result

  • if Proxyman is launched, the websocket opened with the "amqp" (*) subprotocol fails, and the websocket with no suprotocol succeeds
  • if Proxyman is not launched, both websockets connect successfully

(*) the subprotocol doesn't impact the result, but at least "amqp" is a well-known subprotocol from IANA registry

Originally created by @Bertrand on GitHub (Nov 2, 2021). Original GitHub issue: https://github.com/ProxymanApp/Proxyman/issues/1055 Originally assigned to: @NghiaTranUIT on GitHub. ### Proxyman version 23410 ### macOS Version 11.5.2 ### Description We're using a websocket client for graphql subscriptions from a web client. When Proxyman is started, the web browser is unable to connect to the websocket server. This is because the graphql subscription library that we use passes a suprotocol at connect time (namely "graphql-ws"). We've been able to isolate the issue in a very simple node server: [proxyman-ws-sample.zip](https://github.com/ProxymanApp/Proxyman/files/7462858/proxyman-ws-sample.zip) ### Steps to reproduce - unzip the server - npm install - node ./server.js Then - open chrome and navigate to http://0.0.0.0:3000 - open the debug console, network tab ### Result - if Proxyman is launched, the websocket opened with the "amqp" (*) subprotocol fails, and the websocket with no suprotocol succeeds - if Proxyman is **not** launched, both websockets connect successfully (*) the subprotocol doesn't impact the result, but at least "amqp" is a well-known subprotocol from [IANA registry](https://www.iana.org/assignments/websocket/websocket.xml)
Author
Owner

@NghiaTranUIT commented on GitHub (Nov 3, 2021):

Thanks for the detailed report @Bertrand. I love it 👍

I'm able to reproduce it on my side, let me fix it 🙌

<!-- gh-comment-id:958589670 --> @NghiaTranUIT commented on GitHub (Nov 3, 2021): Thanks for the detailed report @Bertrand. I love it 👍 I'm able to reproduce it on my side, let me fix it 🙌
Author
Owner

@NghiaTranUIT commented on GitHub (Nov 3, 2021):

My preliminary investigation shows that Swift-NIO Websocket doesn't support Sec-WebSocket-Protocol: amqp. Therefore, it drops the connection before it sends any message.

<!-- gh-comment-id:958703674 --> @NghiaTranUIT commented on GitHub (Nov 3, 2021): My preliminary investigation shows that Swift-NIO Websocket doesn't support `Sec-WebSocket-Protocol: amqp`. Therefore, it drops the connection before it sends any message.
Author
Owner

@NghiaTranUIT commented on GitHub (Nov 3, 2021):

If you open 0.0.0.0:3000 on Firefox or Safari, it does work. Not sure what causes the problem if we use Chrome

<!-- gh-comment-id:958707481 --> @NghiaTranUIT commented on GitHub (Nov 3, 2021): If you open 0.0.0.0:3000 on Firefox or Safari, it does work. Not sure what causes the problem if we use Chrome
Author
Owner

@NghiaTranUIT commented on GitHub (Nov 3, 2021):

Screen Shot 2021-11-03 at 14 39 16 Screen Shot 2021-11-03 at 14 39 10
<!-- gh-comment-id:958713276 --> @NghiaTranUIT commented on GitHub (Nov 3, 2021): <img width="1405" alt="Screen Shot 2021-11-03 at 14 39 16" src="https://user-images.githubusercontent.com/5878421/140023805-117cb390-2787-42d6-89d9-79c055ffad55.png"> <img width="1274" alt="Screen Shot 2021-11-03 at 14 39 10" src="https://user-images.githubusercontent.com/5878421/140023821-2f2fdc8c-c0da-4e05-97d0-df21d3e5f80d.png">
Author
Owner

@Bertrand commented on GitHub (Nov 3, 2021):

Hello @NghiaTranUIT

I do confirm I'm able to reproduce the issue with swift-nio sample websocket server.
https://github.com/apple/swift-nio/tree/main/Sources/NIOWebSocketServer

At least now we can both investigate the issue :)

<!-- gh-comment-id:958751775 --> @Bertrand commented on GitHub (Nov 3, 2021): Hello @NghiaTranUIT I do confirm I'm able to reproduce the issue with swift-nio sample websocket server. https://github.com/apple/swift-nio/tree/main/Sources/NIOWebSocketServer At least now we can both investigate the issue :)
Author
Owner

@Bertrand commented on GitHub (Nov 3, 2021):

Ok, I've found the issue.

It's due to the websocket server not returning the "elected" subprotocol in the response.

In swift-nio sample, I replaced the upgrader code :

 let upgrader = NIOWebSocketServerUpgrader(shouldUpgrade: { (channel: Channel, head: HTTPRequestHead) in
   channel.eventLoop.makeSucceededFuture(HTTPHeaders()) },
   upgradePipelineHandler: { (channel: Channel, _: HTTPRequestHead) in
                                    channel.pipeline.addHandler(WebSocketTimeHandler())
                                 })

with

let upgrader = NIOWebSocketServerUpgrader(shouldUpgrade: { (channel: Channel, head: HTTPRequestHead) in
    let headers: HTTPHeaders
    if let subprotocols = head.headers.first(name: "Sec-WebSocket-Protocol") {
        headers = HTTPHeaders(
            [("Sec-WebSocket-Protocol", subprotocols)]
        )
    } else {
        headers = HTTPHeaders()
    }
    
    return channel.eventLoop.makeSucceededFuture(headers)
},
upgradePipelineHandler: { (channel: Channel, _: HTTPRequestHead) in
    channel.pipeline.addHandler(WebSocketTimeHandler())
})

This code pretends that the server understand any of the subprotocols requested by the client.

And now everything works fine 😃

In short, Chrome will automatically close a websocket at upgrade time if the server doesn't claim to support one of the suprotocols asked by the client at request time.

The other browsers don't.

Hope this helps you fix the issue on your side.

<!-- gh-comment-id:958774241 --> @Bertrand commented on GitHub (Nov 3, 2021): Ok, I've found the issue. It's due to the websocket server not returning the "elected" subprotocol in the response. In swift-nio sample, I replaced the [upgrader](https://github.com/apple/swift-nio/blob/main/Sources/NIOWebSocketServer/main.swift#L203) code : ```swift let upgrader = NIOWebSocketServerUpgrader(shouldUpgrade: { (channel: Channel, head: HTTPRequestHead) in channel.eventLoop.makeSucceededFuture(HTTPHeaders()) }, upgradePipelineHandler: { (channel: Channel, _: HTTPRequestHead) in channel.pipeline.addHandler(WebSocketTimeHandler()) }) ``` with ```swift let upgrader = NIOWebSocketServerUpgrader(shouldUpgrade: { (channel: Channel, head: HTTPRequestHead) in let headers: HTTPHeaders if let subprotocols = head.headers.first(name: "Sec-WebSocket-Protocol") { headers = HTTPHeaders( [("Sec-WebSocket-Protocol", subprotocols)] ) } else { headers = HTTPHeaders() } return channel.eventLoop.makeSucceededFuture(headers) }, upgradePipelineHandler: { (channel: Channel, _: HTTPRequestHead) in channel.pipeline.addHandler(WebSocketTimeHandler()) }) ``` This code pretends that the server understand any of the subprotocols requested by the client. And now everything works fine 😃 In short, Chrome will automatically close a websocket at upgrade time if the server doesn't claim to support one of the suprotocols asked by the client at request time. The other browsers don't. Hope this helps you fix the issue on your side.
Author
Owner

@NghiaTranUIT commented on GitHub (Nov 3, 2021):

Wow. you're my rescuer @srebalaji, it took me this morning to investigate but I didn't realize it.

I really appreciate it 🙇

Please try this Beta build: https://proxyman.s3.us-east-2.amazonaws.com/beta/Proxyman_2.34.1_Fix_WS_with_subprotocols.dmg

Screen Shot 2021-11-03 at 17 14 11 Screen Shot 2021-11-03 at 17 14 01
<!-- gh-comment-id:958826507 --> @NghiaTranUIT commented on GitHub (Nov 3, 2021): Wow. you're my rescuer @srebalaji, it took me this morning to investigate but I didn't realize it. I really appreciate it 🙇 Please try this Beta build: https://proxyman.s3.us-east-2.amazonaws.com/beta/Proxyman_2.34.1_Fix_WS_with_subprotocols.dmg <img width="1647" alt="Screen Shot 2021-11-03 at 17 14 11" src="https://user-images.githubusercontent.com/5878421/140042830-1459dc8d-21fe-43cc-a8c5-ef432a21dc33.png"> <img width="1283" alt="Screen Shot 2021-11-03 at 17 14 01" src="https://user-images.githubusercontent.com/5878421/140042849-c9a70891-1d8c-47e3-ad13-cf18e21b2428.png">
Author
Owner

@Bertrand commented on GitHub (Nov 4, 2021):

Yes it works perfectly !!!

Many many thanks 😃 😃

<!-- gh-comment-id:960571677 --> @Bertrand commented on GitHub (Nov 4, 2021): Yes it works perfectly !!! Many many thanks 😃 😃
Author
Owner

@tkrajacic commented on GitHub (May 5, 2022):

Did this fix ever make it into a release? If so, this issue can be closed I guess?
(I am investigating why I can't connect to a websocket channel in Firefox when Proxyman is running - cert properly installed and all)

<!-- gh-comment-id:1118365083 --> @tkrajacic commented on GitHub (May 5, 2022): Did this fix ever make it into a release? If so, this issue can be closed I guess? (I am investigating why I can't connect to a websocket channel in Firefox when Proxyman is running - cert properly installed and all)
Author
Owner

@adminfairjungle commented on GitHub (May 5, 2022):

From my standpoint, this problem was fixed then and I never experienced similar issues since.

<!-- gh-comment-id:1118368950 --> @adminfairjungle commented on GitHub (May 5, 2022): From my standpoint, this problem was fixed then and I never experienced similar issues since.
Author
Owner

@NghiaTranUIT commented on GitHub (May 5, 2022):

Yes, this issue is fixed since build 2.35.0 👏

<!-- gh-comment-id:1118408781 --> @NghiaTranUIT commented on GitHub (May 5, 2022): Yes, this issue is fixed since build 2.35.0 👏
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
starred/Proxyman#1049
No description provided.