2019-04-20 19:30:17 -05:00
|
|
|
import D from 'debug';
|
2019-03-17 23:45:48 -05:00
|
|
|
import io from 'socket.io-client';
|
|
|
|
|
2019-04-28 23:45:03 -05:00
|
|
|
import {compose, EventEmitter} from '@avocado/core';
|
2019-04-24 18:01:17 -05:00
|
|
|
|
|
|
|
import {SocketIoParser, allPackets, idFromPacket} from '../packet';
|
2019-03-17 23:45:48 -05:00
|
|
|
|
2019-04-20 19:30:17 -05:00
|
|
|
const debug = D('@avocado:client:socket');
|
|
|
|
|
2019-04-14 16:11:20 -05:00
|
|
|
const decorate = compose(
|
|
|
|
EventEmitter,
|
|
|
|
);
|
|
|
|
|
2019-04-24 18:01:17 -05:00
|
|
|
export class SocketClient extends decorate(class {}) {
|
2019-03-17 23:45:48 -05:00
|
|
|
|
2019-04-12 12:09:05 -05:00
|
|
|
constructor(address, options = {}) {
|
2019-03-17 23:45:48 -05:00
|
|
|
super();
|
2019-04-20 14:13:15 -05:00
|
|
|
this.address = address;
|
2019-04-14 16:11:20 -05:00
|
|
|
this.isConnected = false;
|
2019-04-20 19:30:17 -05:00
|
|
|
this.isReconnecting = false;
|
2019-04-20 14:13:15 -05:00
|
|
|
this.options = {
|
2019-04-11 15:26:13 -05:00
|
|
|
parser: SocketIoParser,
|
2019-03-17 23:45:48 -05:00
|
|
|
path: '/avocado',
|
2019-04-12 00:09:25 -05:00
|
|
|
perMessageDeflate: false,
|
2019-04-20 14:13:15 -05:00
|
|
|
reconnection: false,
|
|
|
|
...options
|
|
|
|
};
|
|
|
|
this.socket = null;
|
|
|
|
this.connect();
|
|
|
|
}
|
|
|
|
|
|
|
|
connect() {
|
|
|
|
if (this.socket) {
|
|
|
|
this.socket.destroy();
|
|
|
|
}
|
|
|
|
this.socket = io(this.address, {
|
|
|
|
...this.options,
|
2019-03-17 23:45:48 -05:00
|
|
|
});
|
|
|
|
this.socket.on('connect', () => {
|
2019-04-20 19:30:17 -05:00
|
|
|
debug('connect');
|
2019-04-14 16:11:20 -05:00
|
|
|
this.isConnected = true;
|
2019-03-17 23:45:48 -05:00
|
|
|
this.emit('connect');
|
|
|
|
});
|
2019-04-20 19:30:17 -05:00
|
|
|
this.socket.on('connect_error', () => {
|
|
|
|
debug('connect_error');
|
|
|
|
this.tryReconnect();
|
|
|
|
});
|
|
|
|
this.socket.on('connect_timeout', () => {
|
|
|
|
debug('connect_timeout');
|
|
|
|
this.tryReconnect();
|
|
|
|
});
|
2019-04-14 16:11:20 -05:00
|
|
|
this.socket.on('disconnect', () => {
|
|
|
|
this.isConnected = false;
|
|
|
|
this.emit('disconnect');
|
2019-04-20 19:30:17 -05:00
|
|
|
debug('disconnect');
|
|
|
|
this.tryReconnect();
|
2019-04-14 16:11:20 -05:00
|
|
|
});
|
2019-04-11 15:26:13 -05:00
|
|
|
for (const Packet of allPackets()) {
|
|
|
|
const id = idFromPacket(Packet);
|
2019-04-25 00:09:03 -05:00
|
|
|
this.socket.on(id, (data) => {
|
|
|
|
this.emit('packet', new Packet(data));
|
2019-04-11 15:26:13 -05:00
|
|
|
});
|
2019-03-17 23:45:48 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-04-20 14:13:15 -05:00
|
|
|
disconnect() {
|
|
|
|
this.socket.disconnect();
|
|
|
|
}
|
|
|
|
|
2019-04-11 15:26:13 -05:00
|
|
|
send(packet) {
|
|
|
|
const id = idFromPacket(packet.constructor);
|
|
|
|
this.socket.emit(id, packet.data);
|
2019-03-17 23:45:48 -05:00
|
|
|
}
|
|
|
|
|
2019-04-20 19:30:17 -05:00
|
|
|
tryReconnect() {
|
|
|
|
if (this.isReconnecting) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
this.isReconnecting = true;
|
|
|
|
let backoff = 100;
|
|
|
|
const tryToReconnect = () => {
|
|
|
|
debug('try to reconnect');
|
|
|
|
if (this.isConnected) {
|
|
|
|
debug('is connected');
|
|
|
|
this.isReconnecting = false;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
this.connect();
|
|
|
|
backoff = Math.min(2000, backoff * 1.5);
|
|
|
|
debug('backoff', backoff);
|
|
|
|
setTimeout(tryToReconnect, backoff);
|
|
|
|
}
|
|
|
|
setTimeout(tryToReconnect, 0);
|
|
|
|
}
|
|
|
|
|
2019-03-17 23:45:48 -05:00
|
|
|
}
|