Polyfill WebSocket with W3C (#32)
* Use W3CSocket from websocket * Handle queueing when not connected (yet) * Assert connection prefixes * Update tests * Bump * Update error checks * Fix polyfill test
This commit is contained in:
+1
-1
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"version": "0.5.12",
|
||||
"version": "0.6.0",
|
||||
"private": true,
|
||||
"workspaces": [
|
||||
"packages/*"
|
||||
|
||||
@@ -37,6 +37,6 @@
|
||||
"@polkadot/util": "^0.6.7",
|
||||
"babel-runtime": "^6.26.0",
|
||||
"isomorphic-fetch": "^2.2.1",
|
||||
"ws": "^3.3.2"
|
||||
"websocket": "^1.0.25"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,9 +14,16 @@ module.exports = class HttpProvider extends JsonRpcCoder implements ProviderInte
|
||||
constructor (endpoint: string) {
|
||||
super();
|
||||
|
||||
assert(endpoint, 'Endpoint should be provided');
|
||||
assert(/^http:\/\//.test(endpoint), `Endpoint should start with 'http://', received '${endpoint}'`);
|
||||
|
||||
this._endpoint = endpoint;
|
||||
}
|
||||
|
||||
get isConnected (): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
async send (method: string, params: Array<any>): Promise<any> {
|
||||
const body = this.encodeJson(method, params);
|
||||
const response = await fetch(this._endpoint, {
|
||||
|
||||
@@ -21,7 +21,30 @@ describe('Http', () => {
|
||||
encodeSpy.mockRestore();
|
||||
decodeSpy.mockRestore();
|
||||
|
||||
mock.done();
|
||||
if (mock) {
|
||||
mock.done();
|
||||
mock = null;
|
||||
}
|
||||
});
|
||||
|
||||
describe('constructor', () => {
|
||||
it('requires the endpoint to be set', () => {
|
||||
expect(
|
||||
() => new Http()
|
||||
).toThrow(/should be provided/);
|
||||
});
|
||||
|
||||
it('requires an http:// prefixed endpoint', () => {
|
||||
expect(
|
||||
() => new Http('ws://')
|
||||
).toThrow(/with 'http/);
|
||||
});
|
||||
});
|
||||
|
||||
describe('isConnected', () => {
|
||||
it('always returns true', () => {
|
||||
expect(http.isConnected).toEqual(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('send', () => {
|
||||
|
||||
@@ -22,5 +22,6 @@ export type JsonRpcResponseBase = {
|
||||
export type JsonRpcResponse = JsonRpcObject & JsonRpcResponseBase;
|
||||
|
||||
export interface ProviderInterface {
|
||||
+isConnected: boolean;
|
||||
send (method: string, params: Array<any>): Promise<any>;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// ISC, Copyright 2017 Jaco Greeff
|
||||
// @flow
|
||||
|
||||
import type { JsonRpcResponse, ProviderInterface } from '../types';
|
||||
import type { JsonRpcRequest, JsonRpcResponse, ProviderInterface } from '../types';
|
||||
|
||||
type AwaitingType = {
|
||||
callback: (error: ?Error, result: any) => void
|
||||
@@ -13,17 +13,23 @@ type WsMessageType = {
|
||||
|
||||
require('./polyfill');
|
||||
|
||||
const assert = require('@polkadot/util/assert');
|
||||
const JsonRpcCoder = require('../jsonRpcCoder');
|
||||
|
||||
module.exports = class WsProvider extends JsonRpcCoder implements ProviderInterface {
|
||||
_autoConnect: boolean = true;
|
||||
_endpoint: string;
|
||||
_handlers: { [number]: AwaitingType } = {};
|
||||
_isConnected: boolean = false;
|
||||
_queued: { [number]: JsonRpcRequest } = {};
|
||||
_websocket: WebSocket;
|
||||
|
||||
constructor (endpoint: string, autoConnect: boolean = true) {
|
||||
super();
|
||||
|
||||
assert(endpoint, 'Endpoint should be provided');
|
||||
assert(/^ws:\/\//.test(endpoint), `Endpoint should start with 'ws://', received '${endpoint}'`);
|
||||
|
||||
this._endpoint = endpoint;
|
||||
this._autoConnect = autoConnect;
|
||||
|
||||
@@ -32,9 +38,11 @@ module.exports = class WsProvider extends JsonRpcCoder implements ProviderInterf
|
||||
}
|
||||
}
|
||||
|
||||
connect = () => {
|
||||
this._handlers = {};
|
||||
get isConnected (): boolean {
|
||||
return this._isConnected;
|
||||
}
|
||||
|
||||
connect = () => {
|
||||
this._websocket = new WebSocket(this._endpoint);
|
||||
|
||||
this._websocket.onclose = this._onClose;
|
||||
@@ -45,6 +53,7 @@ module.exports = class WsProvider extends JsonRpcCoder implements ProviderInterf
|
||||
|
||||
_onClose = () => {
|
||||
// console.log('disconnected from', this._endpoint);
|
||||
this._isConnected = false;
|
||||
|
||||
if (this._autoConnect) {
|
||||
setTimeout(this.connect, 1000);
|
||||
@@ -57,6 +66,16 @@ module.exports = class WsProvider extends JsonRpcCoder implements ProviderInterf
|
||||
|
||||
_onOpen = () => {
|
||||
// console.log('connected to', this._endpoint);
|
||||
this._isConnected = true;
|
||||
|
||||
Object.keys(this._queued).forEach((id: string) => {
|
||||
try {
|
||||
this._websocket.send(this._queued[((id: any): number)]);
|
||||
delete this._queued[((id: any): number)];
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
_onMessage = (message: WsMessageType) => {
|
||||
@@ -82,9 +101,7 @@ module.exports = class WsProvider extends JsonRpcCoder implements ProviderInterf
|
||||
send (method: string, params: Array<any>): Promise<any> {
|
||||
return new Promise((resolve, reject) => {
|
||||
try {
|
||||
this._websocket.send(
|
||||
this.encodeObject(method, params)
|
||||
);
|
||||
const encoded = this.encodeObject(method, params);
|
||||
|
||||
this._handlers[this.id] = {
|
||||
callback: (error: ?Error, result: any) => {
|
||||
@@ -95,6 +112,12 @@ module.exports = class WsProvider extends JsonRpcCoder implements ProviderInterf
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (this._isConnected) {
|
||||
this._websocket.send(encoded);
|
||||
} else {
|
||||
this._queued[this.id] = encoded;
|
||||
}
|
||||
} catch (error) {
|
||||
reject(error);
|
||||
}
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
|
||||
const { mockWs, TEST_WS_URL } = require('../../test/mockWs');
|
||||
|
||||
const { isUndefined } = require('@polkadot/util/is');
|
||||
|
||||
const Ws = require('./index');
|
||||
|
||||
let ws;
|
||||
@@ -12,10 +10,7 @@ let encodeSpy;
|
||||
let decodeSpy;
|
||||
|
||||
function createWs (requests, autoConnect) {
|
||||
mock = autoConnect || isUndefined(autoConnect)
|
||||
? mockWs(requests)
|
||||
: null;
|
||||
|
||||
mock = mockWs(requests);
|
||||
ws = new Ws(TEST_WS_URL, autoConnect);
|
||||
|
||||
encodeSpy = jest.spyOn(ws, 'encodeObject');
|
||||
@@ -26,14 +21,36 @@ function createWs (requests, autoConnect) {
|
||||
|
||||
describe('Ws', () => {
|
||||
afterEach(() => {
|
||||
encodeSpy.mockRestore();
|
||||
decodeSpy.mockRestore();
|
||||
if (encodeSpy) {
|
||||
encodeSpy.mockRestore();
|
||||
encodeSpy = null;
|
||||
}
|
||||
|
||||
if (decodeSpy) {
|
||||
decodeSpy.mockRestore();
|
||||
decodeSpy = null;
|
||||
}
|
||||
|
||||
if (mock) {
|
||||
mock.done();
|
||||
mock = null;
|
||||
}
|
||||
});
|
||||
|
||||
describe('constructor', () => {
|
||||
it('requires the endpoint to be set', () => {
|
||||
expect(
|
||||
() => new Ws()
|
||||
).toThrow(/should be provided/);
|
||||
});
|
||||
|
||||
it('requires an ws:// prefixed endpoint', () => {
|
||||
expect(
|
||||
() => new Ws('http://')
|
||||
).toThrow(/with 'ws/);
|
||||
});
|
||||
});
|
||||
|
||||
describe('websocket', () => {
|
||||
let errorSpy;
|
||||
|
||||
@@ -74,6 +91,10 @@ describe('Ws', () => {
|
||||
expect(ws.connect).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('sets isConnected false', () => {
|
||||
expect(ws.isConnected).toEqual(false);
|
||||
});
|
||||
|
||||
it('does not reconnect when autoConnect false', () => {
|
||||
mock.done();
|
||||
ws = createWs([], false);
|
||||
@@ -108,7 +129,10 @@ describe('Ws', () => {
|
||||
|
||||
describe('send', () => {
|
||||
it('handles internal errors', () => {
|
||||
ws = createWs([], false);
|
||||
ws = createWs([]);
|
||||
|
||||
ws._isConnected = true;
|
||||
ws._websocket = null;
|
||||
|
||||
return ws
|
||||
.send('test_encoding', ['param'])
|
||||
@@ -190,4 +214,35 @@ describe('Ws', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('queued', () => {
|
||||
let sendPromise;
|
||||
|
||||
beforeEach(() => {
|
||||
ws = createWs([{
|
||||
id: 1,
|
||||
method: 'test_queue',
|
||||
reply: {
|
||||
result: 'ok'
|
||||
}
|
||||
}], false);
|
||||
sendPromise = ws.send('test_queue', []);
|
||||
});
|
||||
|
||||
it('sends messages when connected', () => {
|
||||
ws.connect();
|
||||
|
||||
return sendPromise.then((result) => {
|
||||
expect(result).toEqual('ok');
|
||||
});
|
||||
});
|
||||
|
||||
it('sends messages when connected (errors)', () => {
|
||||
ws.connect();
|
||||
|
||||
return sendPromise.then((result) => {
|
||||
expect(result).toEqual('ok');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
// @flow
|
||||
|
||||
if (typeof WebSocket === 'undefined') {
|
||||
global.WebSocket = require('ws');
|
||||
global.WebSocket = require('websocket').w3cwebsocket;
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ describe('ws/polyfill', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
origWs = global.WebSocket;
|
||||
global.fetch = null;
|
||||
global.WebSocket = null;
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
|
||||
@@ -305,10 +305,6 @@ async-each@^1.0.0:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.1.tgz#19d386a1d9edc6e7c1c85d388aedbcc56d33602d"
|
||||
|
||||
async-limiter@~1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.0.tgz#78faed8c3d074ab81f22b4e985d79e8738f720f8"
|
||||
|
||||
async@^1.4.0, async@^1.5.0:
|
||||
version "1.5.2"
|
||||
resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a"
|
||||
@@ -2791,7 +2787,7 @@ is-text-path@^1.0.0:
|
||||
dependencies:
|
||||
text-extensions "^1.0.0"
|
||||
|
||||
is-typedarray@~1.0.0:
|
||||
is-typedarray@^1.0.0, is-typedarray@~1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a"
|
||||
|
||||
@@ -3618,7 +3614,7 @@ mute-stream@0.0.7:
|
||||
version "0.0.7"
|
||||
resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab"
|
||||
|
||||
nan@^2.2.1, nan@^2.3.0:
|
||||
nan@^2.2.1, nan@^2.3.0, nan@^2.3.3:
|
||||
version "2.8.0"
|
||||
resolved "https://registry.yarnpkg.com/nan/-/nan-2.8.0.tgz#ed715f3fe9de02b57a5e6252d90a96675e1f085a"
|
||||
|
||||
@@ -5025,6 +5021,12 @@ type-detect@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-1.0.0.tgz#762217cc06db258ec48908a1298e8b95121e8ea2"
|
||||
|
||||
typedarray-to-buffer@^3.1.2:
|
||||
version "3.1.2"
|
||||
resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-3.1.2.tgz#1017b32d984ff556eba100f501589aba1ace2e04"
|
||||
dependencies:
|
||||
is-typedarray "^1.0.0"
|
||||
|
||||
typedarray@^0.0.6:
|
||||
version "0.0.6"
|
||||
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
|
||||
@@ -5054,10 +5056,6 @@ uid-number@^0.0.6:
|
||||
version "0.0.6"
|
||||
resolved "https://registry.yarnpkg.com/uid-number/-/uid-number-0.0.6.tgz#0ea10e8035e8eb5b8e4449f06da1c730663baa81"
|
||||
|
||||
ultron@~1.1.0:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/ultron/-/ultron-1.1.1.tgz#9fe1536a10a664a65266a1e3ccf85fd36302bc9c"
|
||||
|
||||
unherit@^1.0.4:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/unherit/-/unherit-1.1.0.tgz#6b9aaedfbf73df1756ad9e316dd981885840cd7d"
|
||||
@@ -5218,6 +5216,15 @@ webidl-conversions@^4.0.0:
|
||||
version "4.0.2"
|
||||
resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-4.0.2.tgz#a855980b1f0b6b359ba1d5d9fb39ae941faa63ad"
|
||||
|
||||
websocket@^1.0.25:
|
||||
version "1.0.25"
|
||||
resolved "https://registry.yarnpkg.com/websocket/-/websocket-1.0.25.tgz#998ec790f0a3eacb8b08b50a4350026692a11958"
|
||||
dependencies:
|
||||
debug "^2.2.0"
|
||||
nan "^2.3.3"
|
||||
typedarray-to-buffer "^3.1.2"
|
||||
yaeti "^0.0.6"
|
||||
|
||||
whatwg-encoding@^1.0.1:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-1.0.3.tgz#57c235bc8657e914d24e1a397d3c82daee0a6ba3"
|
||||
@@ -5317,14 +5324,6 @@ write@^0.2.1:
|
||||
dependencies:
|
||||
mkdirp "^0.5.1"
|
||||
|
||||
ws@^3.3.2:
|
||||
version "3.3.2"
|
||||
resolved "https://registry.yarnpkg.com/ws/-/ws-3.3.2.tgz#96c1d08b3fefda1d5c1e33700d3bfaa9be2d5608"
|
||||
dependencies:
|
||||
async-limiter "~1.0.0"
|
||||
safe-buffer "~5.1.0"
|
||||
ultron "~1.1.0"
|
||||
|
||||
x-is-function@^1.0.4:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/x-is-function/-/x-is-function-1.0.4.tgz#5d294dc3d268cbdd062580e0c5df77a391d1fa1e"
|
||||
@@ -5345,6 +5344,10 @@ y18n@^3.2.1:
|
||||
version "3.2.1"
|
||||
resolved "https://registry.yarnpkg.com/y18n/-/y18n-3.2.1.tgz#6d15fba884c08679c0d77e88e7759e811e07fa41"
|
||||
|
||||
yaeti@^0.0.6:
|
||||
version "0.0.6"
|
||||
resolved "https://registry.yarnpkg.com/yaeti/-/yaeti-0.0.6.tgz#f26f484d72684cf42bedfb76970aa1608fbf9577"
|
||||
|
||||
yallist@^2.1.2:
|
||||
version "2.1.2"
|
||||
resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52"
|
||||
|
||||
Reference in New Issue
Block a user