ProxymanApp / Proxyman

Modern. Native. Delightful Web Debugging Proxy for macOS, iOS, and Android ⚡️
https://proxyman.io
5.73k stars 189 forks source link

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

Open Bertrand opened 3 years ago

Bertrand commented 3 years ago

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

Then

Result

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

NghiaTranUIT commented 3 years ago

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

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

NghiaTranUIT commented 3 years ago

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 commented 3 years ago

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 commented 3 years ago
Screen Shot 2021-11-03 at 14 39 16 Screen Shot 2021-11-03 at 14 39 10
Bertrand commented 3 years ago

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 commented 3 years ago

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.

NghiaTranUIT commented 3 years ago

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
Bertrand commented 3 years ago

Yes it works perfectly !!!

Many many thanks 😃 😃

tkrajacic commented 2 years ago

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)

adminfairjungle commented 2 years ago

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

NghiaTranUIT commented 2 years ago

Yes, this issue is fixed since build 2.35.0 👏