twitchax / AspNetCore.Proxy

ASP.NET Core Proxies made easy.
MIT License
522 stars 83 forks source link

Websocket proxy failed "Error during WebSocket handshake" #72

Open martindybal opened 3 years ago

martindybal commented 3 years ago

I'm trying to proxy twilio comunnication. They have sample in doc how to do that with ngix

Sample configuration:

# Proxy requests to /signaling to https://global.vss.twilio.com/signaling
#
    location /signaling {
        proxy_pass https://global.vss.twilio.com/signaling;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "Upgrade";
        proxy_set_header Host $host;
    }

# 
# Proxy requests for the SDK Gateway "/sdkgw" to 'http://sdkgw.us1.twilio.com
#   
    location /sdkgw {
        proxy_pass http://sdkgw.us1.twilio.com;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "Upgrade";
        proxy_set_header Host $host;

I'm tried to use AspNetCore.Proxy but I wasn't succeeded.

            app.UseProxies(proxies =>
            {
                proxies.Map("twilio/signaling", proxy => proxy.UseWs("https://global.vss.twilio.com/signaling"));
                proxies.Map("twilio/sdkgw", proxy => proxy.UseWs("http://sdkgw.us1.twilio.com"));
            });

failed: Error during WebSocket handshake: Unexpected response code: 502

            app.UseProxies(proxies =>
            {
                proxies.Map("twilio/signaling", proxy => proxy.UseWs("wss://global.vss.twilio.com/signaling"));
                proxies.Map("twilio/sdkgw", proxy => proxy.UseWs("wss://sdkgw.us1.twilio.com"));
            });

failed: Error during WebSocket handshake: Unexpected response code: 404

How should I setup AspNetCore.Proxy correctly?

GrumpyBear57 commented 3 years ago

I think I'm having the same issue here, when I try to connect through the ASP proxy, I get a HTTP 502 on my websocket client. I've created very simple test server and clients in node.js to try and debug if it's something we're doing on that end that would interfere, but still can't get it to work. Any direction as to where to go from here would be greatly appreciated.

My server:

const WebSocket = require('ws');

const server = new WebSocket.Server({ port: 8000 });
server.on('connection', socket => {
  console.log(`socket connected`);

  socket.send('who');

  socket.on('message', msg => {
      console.log(`mesage: ${msg}`);
  });

  socket.on('close', () => {
      console.log(`socket closed`);
  });
});

Clients:

const WebSocket = require('ws');

const directSocket = new WebSocket('http://192.168.2.228:8000');
const proxiedSocket = new WebSocket('http://192.168.2.228:5000/kanatran/proxy');

directSocket.on('open', () => {
    console.log('direct socket connection opened');
});

directSocket.on('message', msg => {
    if (msg === 'who')
        directSocket.send('I am direct');
});

directSocket.on('error', console.log);

proxiedSocket.on('open', () => {
    console.log('proxied socket connection opened');  
});

proxiedSocket.on('message', msg => {
    if (msg === 'who')
        proxiedSocket.send('I am proxied');
});

proxiedSocket.on('error', console.log);

ASP Proxy route:

[Route("proxy")]
public Task Proxy() {
    _logger.LogInformation("Proxy request from {RemoteIP}, forwarding", HttpContext.Connection.RemoteIpAddress);
    return this.WsProxyAsync("http://192.168.2.228:8000");
}

Server output:

socket connected
mesage: I am direct

Client output:

direct socket connection opened
Error: Unexpected server response: 502
{stacktrace snipped}

ASP Proxy output:

info: API.Controllers.KanatranController[0]
      Proxy request from ::ffff:192.168.2.228, forwarding
GrumpyBear57 commented 3 years ago

Alright I think I have this figured out now...

First step was to actually enable websockets on the ASP Proxy (add app.UseWebSockets() to startup.cs -- I think this needs to go before app.UseRouting() and/or app.UseEndpoints). This totally makes sense, but it isn't documented in the setup instructions for this package, so I had omitted it.

The next thing I found was that I MUST specify ws://url instead of http://url, both when connecting to the proxy, and when specifying where to proxy to. I'm not entirely sure why this is, since with my normal websocket connections I could use either ws or http protocols, but I digress.

My working client now looks like this:

const proxiedSocket = new WebSocket('ws://192.168.2.228:5000/kanatran/proxy');

And my ASP Proxy route looked like this:

[Route("proxy")]
public Task Proxy() {
    _logger.LogInformation("Proxy request from {RemoteIP}, forwarding", HttpContext.Connection.RemoteIpAddress);
    return this.WsProxyAsync("ws://192.168.2.228:8000");
}

However I now just map it directly in startup.cs, which means I give up logging, but I'm not particularly concerned about that since it was originally for trying to debug this issue anyways.

app.UseProxies(proxies => proxies.Map("kanatran/controller", proxy => proxy.UseWs("ws://localhost:8000")));
twitchax commented 3 years ago

Hi, sorry: just got to this.

So, good call out: I can add that to the README. The Microsoft Docs call this out, but I agree that people using this as a WS proxy likely don't look beyond this package.

As of now, the proxy uses the protocol to determine whether the connection is a websocket or not: there may be a better way to do this.

Is the non-Startup.cs handler not working?

twitchax commented 3 years ago

Leaving this open until I fix the docs.

GrumpyBear57 commented 3 years ago

As of now, the proxy uses the protocol to determine whether the connection is a websocket or not: there may be a better way to do this.

When I was stepping through the decompiled code in the debugger, it looks like you're already doing a check to context.WebSockets.IsWebSocketRequest, which I think should probably be enough, but some further testing may be required for that to ensure proper compatibility.

Is the non-Startup.cs handler not working?

No it is working, I just don't need a full controller for the proxy endpoint, so I opted to do it in startup.cs instead of a dedicated controller class.