pladaria / reconnecting-websocket

Reconnecting WebSocket. For Web, React Native, cli (Node.js)
MIT License
1.22k stars 197 forks source link

WebSocket creation callback #138

Open borkaborka opened 4 years ago

borkaborka commented 4 years ago

Hello,

Currently, the only way to supply non-default WebSocket implementation (e.g. 'ws' in Node.js) - is to pass a custom constructor via options. This has a limitation that a constructor must comply with one of the 2 variants:

new WebSocket(url, this._protocols) new WebSocket(url)

However, 'ws' has a 3rd very important variant:

constructor(address: string, protocols?: string | string[], options?: WebSocket.ClientOptions)

https://github.com/websockets/ws/blob/88d0345997ea14b262519c7d5b5baaf6f9d78035/lib/websocket.js#L43

Where options is something like this:

interface ClientOptions {
    protocol?: string;
    handshakeTimeout?: number;
    perMessageDeflate?: boolean | PerMessageDeflateOptions;
    localAddress?: string;
    protocolVersion?: number;
    headers?: { [key: string]: string };
    origin?: string;
    agent?: http.Agent;
    host?: string;
    family?: number;
    checkServerIdentity?(servername: string, cert: CertMeta): boolean;
    rejectUnauthorized?: boolean;
    passphrase?: string;
    ciphers?: string;
    cert?: CertMeta;
    key?: CertMeta;
    pfx?: string | Buffer;
    ca?: CertMeta;
    maxPayload?: number;
}

Note that there are some super-important options like TLS parameters etc. Currently, the only way to solve this - is to implement a custom ws-derived class where one had hard-code relevant parameters. But what if I can't hard-code them and I need to pass different values from the caller? There seems to be no way to do it.

A possible solution is to add ability to pass a "WebSocket creation callback", i.e. a function which will return a WebSocket-resembling object. This way, the construction work moves to the caller allowing creation of WebSocket object in any way desirable

In other words, instead of this line: https://github.com/pladaria/reconnecting-websocket/blob/05a2f7cb0e31f15dff5ff35ad53d07b1bec5e197/reconnecting-websocket.ts#L382-L384

write something like: (assuming createWebSocket is a function passed in options)

this._ws = createWebSocket(url, this_protocols, options)

Thank you.

devshorts commented 4 years ago

Yeah a websocket factory would be nice. I think that's more useful than a url factory

alexsegura commented 4 years ago

You can do it like this 😉

function createWebSocketClass(options) {
  return class extends WebSocket {
    constructor(url, protocols) {
      super(url, protocols, options)
    }
  }
}

const token = '123456'

const rws = new ReconnectingWebSocket(url, '', {
  WebSocket: createWebSocketClass({
    headers: {
      Authorization: `Bearer ${token}`,
    },
  })
})

@pladaria should I add this to the documentation?

borkaborka commented 4 years ago

Thanks @alexsegura. Indeed, this nice idea works. Though I would still prefer an explicit createWebSocket method in options, rather than this somewhat "hacky" method. It would also eliminate the need to document a (admittedly clever) workaround, because it would make this common usage more obvious.