0.1.6
major refactor of websocket client remove auto reconnect dependency and wrote zero dep one. Has promise for initial connect and then will reconnect emmiting disconnet/reconnect to server eventsmaster
parent
4d6661f785
commit
2627de964d
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@uci/websocket-client",
|
"name": "@uci/websocket-client",
|
||||||
"version": "0.1.5",
|
"version": "0.1.6",
|
||||||
"description": "JSON packet browser client over web socket",
|
"description": "JSON packet browser client over web socket",
|
||||||
"main": "src",
|
"main": "src",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
@ -29,7 +29,8 @@
|
||||||
"devDependencies": {},
|
"devDependencies": {},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"auto-bind": "^2.0.0",
|
"auto-bind": "^2.0.0",
|
||||||
"eventemitter3": "^3.1.0",
|
"await-to-js": "^2.1.1",
|
||||||
"reconnecting-websocket": "^4.1.10"
|
"better-try-catch": "^0.6.2",
|
||||||
|
"eventemitter3": "^3.1.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,9 @@
|
||||||
// Websocket is a native global for vanilla JS
|
// Websocket is a native global for vanilla JS
|
||||||
// /* globals WebSocket:true */
|
/* globals WebSocket:true */
|
||||||
|
|
||||||
import btc from 'better-try-catch'
|
import btc from 'better-try-catch'
|
||||||
import EventEmitter from 'eventemitter3'
|
import EventEmitter from 'eventemitter3'
|
||||||
import autoBind from 'auto-bind'
|
import autoBind from 'auto-bind'
|
||||||
import WS from 'reconnecting-websocket'
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Web Socket Consumer - An in browser consumer/client that can communicate via UCI packets
|
* Web Socket Consumer - An in browser consumer/client that can communicate via UCI packets
|
||||||
|
@ -12,6 +11,9 @@ import WS from 'reconnecting-websocket'
|
||||||
* uses the browser built in vanilla js global {@link https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/WebSocket Websocket client class}
|
* uses the browser built in vanilla js global {@link https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/WebSocket Websocket client class}
|
||||||
* @extends EventEmitter
|
* @extends EventEmitter
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
let count = 1
|
||||||
|
|
||||||
class WSConsumer extends EventEmitter {
|
class WSConsumer extends EventEmitter {
|
||||||
/**
|
/**
|
||||||
* constructor - Description
|
* constructor - Description
|
||||||
|
@ -24,9 +26,11 @@ class WSConsumer extends EventEmitter {
|
||||||
this.name = opts.name || 'browser'
|
this.name = opts.name || 'browser'
|
||||||
this.instanceID = new Date().getTime()
|
this.instanceID = new Date().getTime()
|
||||||
this.url = url
|
this.url = url
|
||||||
this.wsopts = opts.ws || { maxReconnectionDelay: 10000,minReconnectionDelay: 1000 + Math.random() * 4000,reconnectionDelayGrowFactor: 1.3,minUptime: 5000,connectionTimeout: 4000,maxRetries: Infinity,debug: false,}
|
this.rsLimit = opts.rsLimit || 5
|
||||||
|
this.initTimeout = opts.initTimeout || 30000
|
||||||
|
this.rsDelay = opts.rsDelay || 5000 // in large production you'd want a more robust delay calculation
|
||||||
this.protocol = opts.protocol // available if needed but not documented
|
this.protocol = opts.protocol // available if needed but not documented
|
||||||
this.socket = {}
|
this.started = false
|
||||||
autoBind(this)
|
autoBind(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,44 +42,58 @@ class WSConsumer extends EventEmitter {
|
||||||
* but opted to use https://www.npmjs.com/package/reconnecting-websocket
|
* but opted to use https://www.npmjs.com/package/reconnecting-websocket
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// async connect () {
|
|
||||||
// return new Promise((resolve, reject) => {
|
|
||||||
// const init = (socket) => {
|
|
||||||
// if (socket) console.error('Disconnected from Server')
|
|
||||||
// this.socket = new WebSocket(this.url, this.protocol)
|
|
||||||
// this.socket.addEventListener('close', init)
|
|
||||||
// this.socket.addEventListener('open', open.bind(this))
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// setTimeout(function () {
|
|
||||||
// reject(new Error('Socket did not initially connect in 20 seconds'))
|
|
||||||
// }, 20000)
|
|
||||||
//
|
|
||||||
// init()
|
|
||||||
//
|
|
||||||
// function open () {
|
|
||||||
// this.listen()
|
|
||||||
// resolve(`socket open to server at : ${this.url}`)
|
|
||||||
// }
|
|
||||||
// })
|
|
||||||
// }
|
|
||||||
|
|
||||||
async connect () {
|
async connect () {
|
||||||
|
// console.log('--- initial connect to websocket at', this.url)
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
this.socket = new WS(this.url, this.protocol, this.wsopts)
|
if(!this.url) reject('no url provided!')
|
||||||
|
let timeout
|
||||||
|
let connect = con.bind(this)
|
||||||
|
|
||||||
|
function con () {
|
||||||
|
// console.log(this.started, this.socket)
|
||||||
|
if (this.socket) delete this.socket // make sure previous socket is garabage collected
|
||||||
|
this.socket = new WebSocket(this.url, this.protocol)
|
||||||
|
// console.log('ready after create', this.socket.readyState, this.socket.onopen, this.socket.onclose)
|
||||||
this.socket.onopen = open.bind(this)
|
this.socket.onopen = open.bind(this)
|
||||||
|
|
||||||
setTimeout(function () {
|
timeout = setTimeout(function () {
|
||||||
reject(new Error('Socket did not initially connect in 20 seconds'))
|
if (!this.started && count===1) console.log('original connection connect failed - retrying')
|
||||||
}, 20000)
|
console.log(`socket has not ${this.started?'re':''}connected in ${this.rsDelay*count/1000} seconds`)
|
||||||
// this.socket.onerror = (ev) => { reject(`could not connect/reconnect to server : ${ev}`)}
|
count += 1
|
||||||
|
if (!this.started && this.rsDelay*count > this.initTimeout) {
|
||||||
function open () {
|
let err = `unable to make a connection to websocket server at ${this.url} within ${this.initTimeout/1000}s`
|
||||||
this.listen()
|
console.log(err)
|
||||||
resolve(`socket open to server at : ${this.url}`)
|
reject({url:this.url, msg:err})
|
||||||
|
}
|
||||||
|
else connect()
|
||||||
|
}.bind(this), this.rsDelay)
|
||||||
}
|
}
|
||||||
|
|
||||||
})
|
function open () {
|
||||||
|
this.listen() // this handles messages
|
||||||
|
console.log(`socket open to server at : ${this.url}`)
|
||||||
|
if (!this.started) this.emit('connected')
|
||||||
|
else this.emit('reconnected')
|
||||||
|
clearTimeout(timeout)
|
||||||
|
// this.socket.onerror = error.bind(this)
|
||||||
|
this.socket.onclose = close.bind(this)
|
||||||
|
console.log(this.socket)
|
||||||
|
count = 0
|
||||||
|
this.started = true
|
||||||
|
resolve({url:this.url, msg:`socket open to server at : ${this.url}`})
|
||||||
|
}
|
||||||
|
|
||||||
|
connect() // get the ball rolling
|
||||||
|
|
||||||
|
function close () {
|
||||||
|
this.socket.onclose = null
|
||||||
|
console.error('Socket has closed, attempting reconnect')
|
||||||
|
this.removeAllListeners('push')
|
||||||
|
this.socket.onmessage = null
|
||||||
|
this.emit('disconnected')
|
||||||
|
connect()
|
||||||
|
}
|
||||||
|
}) // end promise
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -86,7 +104,9 @@ class WSConsumer extends EventEmitter {
|
||||||
* @returns {type} Description
|
* @returns {type} Description
|
||||||
*/
|
*/
|
||||||
listen (func) {
|
listen (func) {
|
||||||
this.socket.addEventListener('message', handler.bind(this))
|
this.socket.onmessage = packetHandler.bind(this)
|
||||||
|
|
||||||
|
// process 'pushed' packets
|
||||||
this.on('pushed', async function (packet) {
|
this.on('pushed', async function (packet) {
|
||||||
// TODO do some extra security here for 'evil' pushed packets
|
// TODO do some extra security here for 'evil' pushed packets
|
||||||
let res = await this._packetProcess(packet)
|
let res = await this._packetProcess(packet)
|
||||||
|
@ -98,7 +118,7 @@ class WSConsumer extends EventEmitter {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
function handler (event) {
|
function packetHandler (event) {
|
||||||
let packet = {}
|
let packet = {}
|
||||||
if (this.socket.readyState === 1) {
|
if (this.socket.readyState === 1) {
|
||||||
let [err, parsed] = btc(JSON.parse)(event.data)
|
let [err, parsed] = btc(JSON.parse)(event.data)
|
||||||
|
@ -110,7 +130,9 @@ class WSConsumer extends EventEmitter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// console.log('in the handler', event.data)
|
// console.log('in the handler', event.data)
|
||||||
if (func) func(packet)
|
if (func) func(packet) // extra processing if enabled
|
||||||
|
// this is response to a packet send command listener and is processed below
|
||||||
|
// will also emit 'pushed' via id which can be listened for in app
|
||||||
this.emit(packet._header.id, packet)
|
this.emit(packet._header.id, packet)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -165,6 +187,7 @@ class WSConsumer extends EventEmitter {
|
||||||
this._packetProcess = func
|
this._packetProcess = func
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// do nothing
|
||||||
async _packetProcess (packet) {
|
async _packetProcess (packet) {
|
||||||
return Promise.resolve(packet)
|
return Promise.resolve(packet)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue