Search code examples
javascriptgoogle-chrome-extension

JavaScript WebSocket class


I need to log WebSocket traffic, so I am about to write a Chrome Extension that would inject some JavaScript code that should do a trick.

WebSocket = new Proxy(WebSocket, {
  construct: (target, args) => {
    console.log('new WebSocket instance');
    return new target(...args);
  }
})

What method do I need to override to log messages sent and received?

How can I modify such messages sent/received? A simple working code would be greatly appreciated.


Solution

  • Rather than a Proxy, I'd construct and return a plain WebSocket, with your own custom methods attached as needed:

    const origWebSocket = window.WebSocket;
    window.WebSocket = function(url) {
      const socket = new origWebSocket(url);
      socket.send = (msg) => {
        console.log('intercepted send:', msg);
        origWebSocket.prototype.send.call(socket, msg);
      };
      socket.addEventListener('message', (event) => {
        console.log('intercepted receive:', event.data);
      });
      return socket;
    };
    
    // patching done
    const socket = new WebSocket('wss://echo.websocket.org');
    socket.onopen = () => {
      socket.send('hi');
    };

    To also modify an incoming message, you can patch the onmessage property of the instance (and the addEventListener property too, if needed) so that, when a message is received, it goes through your custom function first before the user's callback runs:

    const origWebSocket = window.WebSocket;
    const origOnMessage = Object.getOwnPropertyDescriptor(WebSocket.prototype, 'onmessage').set;
    window.WebSocket = function(url) {
      const socket = new origWebSocket(url);
      Object.defineProperty(socket, 'onmessage', {
        set(cb) {
          origOnMessage.call(socket, (e) => {
            // Have to use defineProperty because `data` is a getter on the prototype
            Object.defineProperty(e, 'data', { value: e.data + ' modified' });
            cb(e);
          });
        }
      });
      socket.send = (msg) => {
        console.log('intercepted send:', msg);
        origWebSocket.prototype.send.call(socket, msg);
      };
      socket.addEventListener('message', (event) => {
        console.log('intercepted receive:', event.data);
        event.data = 'foobar';
      });
      return socket;
    };
    
    // patching done
    const socket = new WebSocket('wss://echo.websocket.org');
    socket.onopen = () => {
      socket.send('hi');
    };
    socket.onmessage = e => console.log('consumer sees message:', e.data);