ruffle-rs / ruffle

A Flash Player emulator written in Rust
https://ruffle.rs
Other
15.75k stars 817 forks source link

Implement tcp socket handler api #16674

Open freehuntx opened 5 months ago

freehuntx commented 5 months ago

Describe the feature Additionally to "socketProxy" it would be nice to be able to add custom socket handler that lets us implement the tcp server logic ourself.
Effectively mocking the TCP connection.

Workaround Currently we can set a socketProxy and hook the window.WebSocket class to achieve this.

window.oWebSocket = window.oWebSocket || window.WebSocket;
window.WebSocket = function (url, protocols) {
  if (!/pengu\.test/.test(url)) { // Add a check which decides if we wanna hook or use the real WebSocket implementation
    return new window.oWebSocket(url, protocols);
  }

  const socket = {
    _events: new EventTarget(),
    _closed: false,

    addEventListener(...args) {
      return this._events.addEventListener(...args);
    },

    removeEventListener(...args) {
      return this._events.removeEventListener(...args);
    },

    dispatchEvent(...args) {
      return this._events.dispatchEvent(...args);
    },

    send(data) {
      if (this._closed) {
        throw new Error('WebSocket is closed');
      }

      console.log("Send: ", data); // Intercept what the client tries to send.
    },
    close(code, reason) {
      this._closed = true;
      const event = new Event('close');
      event.code = code || 1000;
      event.reason = reason || '';
      this.dispatchEvent(event);
    },
    get readyState() {
      return this._closed ? 3 : 1; // 3 = CLOSED, 1 = OPEN
    },

    get bufferedAmount() {
      return 0;
    },
    get binaryType() {
      return 'blob';
    },
    set binaryType(type) {},
    get url() {
      return url;
    },
    get protocol() {
      return protocols
        ? Array.isArray(protocols)
          ? protocols.join(',')
          : protocols
        : '';
    },
    open() {
      setTimeout(() => {
        const event = new Event('open');
        this.dispatchEvent(event);
      }, 10);
    },
  };

  // Mimic connection open
  setTimeout(() => {
    socket.open();
  }, 10);

  return socket;
};

Proposal

function exampleHandler() {
  return {
    host: "some.hostname.org",
    port: 1234,
    async connect(ctx, details) {
      console.log("Connecting to:", details.host, details.port)
      await new Promise(resolve => setTimeout(resolve, 1000) // Simulate network delay
      return true // Indicates an open connection
    },
    close(ctx, details) {
      console.log("Connection closed:", details)
    },
    async send(ctx, data) {
      console.log("Send:", data)
      await new Promise(resolve => setTimeout(resolve, 1000) // Simulate network delay
      ctx.recv("result data")
    }
  }
}

window.RufflePlayer.config = {
  socketHandler: [
    exampleHandler
  ]
}
freehuntx commented 5 months ago

Note:
The above "workaround" is already working so it should be possible to add this feature. You can see it in action here: https://github.com/freehuntx/bomberpengu

This workaround messes with js internals which is not a good idea. So a proper api would be a cleaner solution.