Proxyman icon indicating copy to clipboard operation
Proxyman copied to clipboard

[Bug] Proxyman breaks web sockets when websocket subprotocols are specified

Open Bertrand opened this issue 4 years ago • 11 comments

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

  • 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

Bertrand avatar Nov 02 '21 17:11 Bertrand

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

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

NghiaTranUIT avatar Nov 03 '21 01:11 NghiaTranUIT

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.

NghiaTranUIT avatar Nov 03 '21 07:11 NghiaTranUIT

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

NghiaTranUIT avatar Nov 03 '21 07:11 NghiaTranUIT

Screen Shot 2021-11-03 at 14 39 16 Screen Shot 2021-11-03 at 14 39 10

NghiaTranUIT avatar Nov 03 '21 07:11 NghiaTranUIT

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 :)

Bertrand avatar Nov 03 '21 08:11 Bertrand

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.

Bertrand avatar Nov 03 '21 09:11 Bertrand

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

NghiaTranUIT avatar Nov 03 '21 10:11 NghiaTranUIT

Yes it works perfectly !!!

Many many thanks 😃 😃

Bertrand avatar Nov 04 '21 09:11 Bertrand

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)

tkrajacic avatar May 05 '22 09:05 tkrajacic

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

adminfairjungle avatar May 05 '22 09:05 adminfairjungle

Yes, this issue is fixed since build 2.35.0 👏

NghiaTranUIT avatar May 05 '22 10:05 NghiaTranUIT