import D from 'debug'; import io from 'socket.io-client'; import {compose, EventEmitter} from '@avocado/core'; import {SocketIoParser, allPackets, idFromPacket} from '../packet'; const debug = D('@avocado/net:socket'); const decorate = compose( EventEmitter, ); export class SocketClient extends decorate(class {}) { constructor(address, options = {}) { super(); this.address = address; this.isConnected = false; this.isReconnecting = false; this.options = { parser: SocketIoParser, path: '/avocado', perMessageDeflate: false, reconnection: false, ...options }; this.socket = null; this.connect(); } connect() { if (this.socket) { this.socket.destroy(); } this.socket = io(this.address, { ...this.options, }); this.socket.on('connect', () => { debug('connect'); this.isConnected = true; this.emit('connect'); }); this.socket.on('connect_error', () => { debug('connect_error'); this.tryReconnect(); }); this.socket.on('connect_timeout', () => { debug('connect_timeout'); this.tryReconnect(); }); this.socket.on('disconnect', () => { this.isConnected = false; this.emit('disconnect'); debug('disconnect'); this.tryReconnect(); }); for (const Packet of allPackets()) { const id = idFromPacket(Packet); this.socket.on(id, (data) => { const packet = new Packet(data); debug('recieved packet %o', packet); this.emit('packet', packet); }); } } disconnect() { this.socket.disconnect(); } get id() { return this.socket ? this.socket.id : undefined; } send(packet) { debug('sending packet %o', packet); const id = idFromPacket(packet.constructor); this.socket.binary(true).emit(id, packet.data); } get session() { return this.socket.handshake.session; } to(channel) { return { send: (packet) => { const id = idFromPacket(packet.constructor); this.socket.binary(true).to(channel).emit(id, packet.data); }, }; } 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); } }