Skip to content

Commit

Permalink
feat: make protocols updatable
Browse files Browse the repository at this point in the history
  • Loading branch information
knows committed May 19, 2020
1 parent 05a2f7c commit 75c3473
Show file tree
Hide file tree
Showing 3 changed files with 110 additions and 9 deletions.
29 changes: 28 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,33 @@ const urlProvider = async () => {
const rws = new ReconnectingWebSocket(urlProvider);
```

### Update Protocols

The `protocols` parameter will be resolved before connecting, possible types:

- `null`
- `string`
- `string[]`
- `() => string | string[] | null`
- `() => Promise<string | string[] | null>`

```javascript
import ReconnectingWebSocket from 'reconnecting-websocket`;
const rws = new ReconnectingWebSocket('ws://your.site.com', 'your protocol');
```

```javascript
import ReconnectingWebSocket from 'reconnecting-websocket`;
const protocols = ['p1', 'p2', ['p3.1', 'p3.2']];
let protocolsIndex = 0;
// round robin protocols provider
const protocolsProvider = () => protocols[protocolsIndex++ % protocols.length];
const rws = new ReconnectingWebSocket('ws://your.site.com', protocolsProvider);
```

### Options

#### Sample with custom options
Expand Down Expand Up @@ -130,7 +157,7 @@ debug: false,
### Methods

```typescript
constructor(url: UrlProvider, protocols?: string | string[], options?: Options)
constructor(url: UrlProvider, protocols?: ProtocolsProvider, options?: Options)

close(code?: number, reason?: string)
reconnect(code?: number, reason?: string)
Expand Down
39 changes: 39 additions & 0 deletions __tests__/test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,45 @@ test('null websocket protocol', done => {
});
});

test('websocket invalid protocolsProvider', () => {
const ws = new ReconnectingWebSocket('ws://example.com', 'foo', {});

// @ts-ignore - accessing private property
expect(() => ws._getNextProtocols(() => /Hahaha/)).toThrow();
});

test('websocket sync protocolsProvider', done => {
const anyProtocol = 'bar';
const wss = new WebSocketServer({port: PORT});

const ws = new ReconnectingWebSocket(URL, () => anyProtocol, {});
ws.addEventListener('open', () => {
expect(ws.url).toBe(URL);
expect(ws.protocol).toBe(anyProtocol);
ws.close();
});

ws.addEventListener('close', () => {
wss.close(() => setTimeout(done, 100));
});
});

test('websocket async protocolsProvider', done => {
const anyProtocol = 'foo';
const wss = new WebSocketServer({port: PORT});

const ws = new ReconnectingWebSocket(URL, async () => anyProtocol, {});
ws.addEventListener('open', () => {
expect(ws.url).toBe(URL);
expect(ws.protocol).toBe(anyProtocol);
ws.close();
});

ws.addEventListener('close', () => {
wss.close(() => setTimeout(done, 100));
});
});

test('connection status constants', () => {
const ws = new ReconnectingWebSocket(URL, undefined, {maxRetries: 0});

Expand Down
51 changes: 43 additions & 8 deletions reconnecting-websocket.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,12 @@ const DEFAULT = {
};

export type UrlProvider = string | (() => string) | (() => Promise<string>);
export type ProtocolsProvider =
| null
| string
| string[]
| (() => string | string[] | null)
| (() => Promise<string | string[] | null>);

export type Message = string | ArrayBuffer | Blob | ArrayBufferView;

Expand Down Expand Up @@ -77,10 +83,10 @@ export default class ReconnectingWebSocket {
private _messageQueue: Message[] = [];

private readonly _url: UrlProvider;
private readonly _protocols?: string | string[];
private readonly _protocols?: ProtocolsProvider;
private readonly _options: Options;

constructor(url: UrlProvider, protocols?: string | string[], options: Options = {}) {
constructor(url: UrlProvider, protocols?: ProtocolsProvider, options: Options = {}) {
this._url = url;
this._protocols = protocols;
this._options = options;
Expand Down Expand Up @@ -330,6 +336,32 @@ export default class ReconnectingWebSocket {
});
}

private _getNextProtocols(
protocolsProvider: ProtocolsProvider | null,
): Promise<string | string[] | null> {
if (!protocolsProvider) return Promise.resolve(null);

if (typeof protocolsProvider === 'string' || Array.isArray(protocolsProvider)) {
return Promise.resolve(protocolsProvider);
}

if (typeof protocolsProvider === 'function') {
const protocols = protocolsProvider();
if (!protocols) return Promise.resolve(null);

if (typeof protocols === 'string' || Array.isArray(protocols)) {
return Promise.resolve(protocols);
}

// @ts-ignore redundant check
if (protocols.then) {
return protocols;
}
}

throw Error('Invalid protocols');
}

private _getNextUrl(urlProvider: UrlProvider): Promise<string> {
if (typeof urlProvider === 'string') {
return Promise.resolve(urlProvider);
Expand Down Expand Up @@ -372,16 +404,19 @@ export default class ReconnectingWebSocket {
throw Error('No valid WebSocket class provided');
}
this._wait()
.then(() => this._getNextUrl(this._url))
.then(url => {
.then(() =>
Promise.all([
this._getNextUrl(this._url),
this._getNextProtocols(this._protocols || null),
]),
)
.then(([url, protocols]) => {
// close could be called before creating the ws
if (this._closeCalled) {
return;
}
this._debug('connect', {url, protocols: this._protocols});
this._ws = this._protocols
? new WebSocket(url, this._protocols)
: new WebSocket(url);
this._debug('connect', {url, protocols});
this._ws = protocols ? new WebSocket(url, protocols) : new WebSocket(url);
this._ws!.binaryType = this._binaryType;
this._connectLock = false;
this._addListeners();
Expand Down

0 comments on commit 75c3473

Please sign in to comment.