electron / electron

:electron: Build cross-platform desktop apps with JavaScript, HTML, and CSS
https://electronjs.org
MIT License
114.19k stars 15.4k forks source link

[Bug]: Websocket connection in renderer doesn't go through proxy even when session.setProxy used #34810

Closed Bojack92160 closed 2 years ago

Bojack92160 commented 2 years ago

Preflight Checklist

Electron Version

18.3.0

What operating system are you using?

Windows

Operating System Version

Windows 10

What arch are you using?

x64

Last Known Working Electron version

don't know

Expected Behavior

I would like to make my websocket connection (as a client) go through a proxy. For that, I create my websocket in a browser window, and use setProxy on the window.

When I use setProxy on the session of a browser window, I expect it to proxify all connections.

Actual Behavior

The websocket is well opened but does not go through the proxy. In fact, the 'login' event is not even called. I have tried to use webContents.session.forceReloadProxyConfig() wihtout success. If I use loadUrl to go for example on 'https://whatismyipaddress.com/', this request go though the proxy, and the login event is called

Testcase Gist URL

No response

Additional Information

Environment versions: node 16.15.1 ws 8.6.0

Code: Here is my code to reproduce:

In the main process:

const { BrowserWindow, ipcMain } = require('electron');

this.win = new BrowserWindow({
    width: 1000,
    height: 600,
    webPreferences: {
        devTools: true,
        nodeIntegration: true,
        contextIsolation: false
    }
});

this.win.webContents.on('login', (event, webContents, request, callback) => {
    console.log(' WM  login with user and password');
    callback('username', 'password');
    this.win.webContents.openDevTools();
});
this.win.webContents.session.setProxy({ proxyRules: 'http://myProxyIp:myProxyPort' });
this.win.loadUrl('./index.html');
this.win.webContents.send('openWs', ['wsUrl']); //method to open ws in render windows

ipcMain.on('error', (event, arg) => {
    console.log(arg);
});

ipcMain.on('close', (event, arg) => {
    console.log(arg);
});
ipcMain.on('message', (event, arg) => {
    console.log(arg)
});
ipcMain.on('message', (event, arg) => {
                console.log(arg)
});

The render process:

index.html:


<!DOCTYPE html>
  <html>
    <head>
      <meta charset="UTF-8">
      <title>Hello World!</title>
    </head>
    <body>
      <h1>Hello World!</h1>
      We are using node <script>document.write(process.versions.node)</script>,
      Chrome <script>document.write(process.versions.chrome)</script>,
      and Electron <script>document.write(process.versions.electron)</script>.
    </body>
    <script>
        require('./renderWs.js');
    </script>
  </html>

renderWs.js :

const Websocket = require('ws');
const ipc = require('electron').ipcRenderer;

ipc.on('openWs', (event, args) => {
    console.log(event, args);
    openWS(...args);
});

async function openWS(url) {

    const ws = new Websocket(url);

    ws.on('open', () => {
        ipc.send('open', 'open');
        console.log('open');

        ipc.on('send', (event, msg) => {
            console.log(event, msg);
            ws.send(msg);
        });
    });

    ws.on('error', (e) => {
        ipc.send('error', e);
        console.log('error', e);
    });

    ws.on('close', (code) => {
        ipc.send('close', close);
        console.log('close', code);
    });

    ws.on('message', (data, isBinary) => {

    ipc.send('message', [data, isBinary]);
        console.log('message', !isBinary ? data.toString() : data);
    });

}
Prinzhorn commented 2 years ago

You are using the ws package and not the native WebSocket. So this is not a browser API but a Node.js thing (different HTTP stack from the renderer/browser). It can be proxied according to their docs https://www.npmjs.com/package/ws#how-to-connect-via-a-proxy but you shouldn't need that package at all.

Bojack92160 commented 2 years ago

thanks for the answer, this explain this behaviour