import tui from './httpRequest' const socket = { connected: false, task: null, heartbeatTimer: null, subscriptions: {}, connecting: false, messageHandlers: [], ackHandlers: [], presenceHandlers: [], roomEndHandlers: [], offlineHandlers: [] } function wsUrl() { return tui.interfaceUrl().replace(/^http/, 'ws') + '/ws/websocket' } function frame(command, headers = {}, body = '') { const lines = [command] Object.keys(headers).forEach(key => { lines.push(`${key}:${headers[key]}`) }) lines.push('') lines.push(body) return lines.join('\n') + '\u0000' } function parseFrame(raw) { const text = raw.replace(/\u0000/g, '') const splitIndex = text.indexOf('\n\n') const head = splitIndex >= 0 ? text.slice(0, splitIndex) : text const body = splitIndex >= 0 ? text.slice(splitIndex + 2) : '' const lines = head.split('\n') const command = lines.shift() const headers = {} lines.forEach(line => { const index = line.indexOf(':') if (index > -1) headers[line.slice(0, index)] = line.slice(index + 1) }) return { command, headers, body } } function emit(list, payload) { list.forEach(handler => { try { handler(payload) } catch (e) { console.warn('ie socket handler error', e) } }) } function send(destination, body = {}) { if (!socket.connected || !socket.task) return false socket.task.send({ data: frame('SEND', { destination, 'content-type': 'application/json' }, JSON.stringify(body)) }) return true } function subscribe(destination, id) { socket.task.send({ data: frame('SUBSCRIBE', { id, destination, ack: 'auto' }) }) } function subscribeRoom(roomId) { if (!socket.connected || !socket.task || !roomId) return false const id = 'ie-room-' + roomId if (socket.subscriptions[id]) return true socket.subscriptions[id] = true subscribe('/topic/ie/room/' + roomId, id) return true } function bindSubscriptions() { subscribe('/user/queue/ie/ack', 'ie-ack') subscribe('/user/queue/ie/message', 'ie-message') subscribe('/user/queue/ie/presence', 'ie-presence') subscribe('/user/queue/ie/room-end', 'ie-room-end') subscribe('/user/queue/ie/offline', 'ie-offline') send('/app/ie/connect') } function handleMessage(raw) { if (raw === 'h') return const data = parseFrame(raw) if (data.command === 'CONNECTED') { socket.connected = true bindSubscriptions() startHeartbeat() return } if (data.command !== 'MESSAGE') return let payload = data.body try { payload = JSON.parse(data.body) } catch (e) {} const dest = data.headers.destination || '' if (dest.indexOf('/queue/ie/ack') > -1) emit(socket.ackHandlers, payload) if (dest.indexOf('/queue/ie/message') > -1 || dest.indexOf('/topic/ie/room/') > -1) emit(socket.messageHandlers, payload) if (dest.indexOf('/queue/ie/presence') > -1) emit(socket.presenceHandlers, payload) if (dest.indexOf('/queue/ie/room-end') > -1) emit(socket.roomEndHandlers, payload) if (dest.indexOf('/queue/ie/offline') > -1) emit(socket.offlineHandlers, payload) } function startHeartbeat() { clearInterval(socket.heartbeatTimer) socket.heartbeatTimer = setInterval(() => { send('/app/ie/heartbeat') }, 25000) } function connect() { if (socket.connected || socket.connecting) return Promise.resolve() socket.connecting = true return new Promise((resolve, reject) => { socket.task = uni.connectSocket({ url: wsUrl(), header: { accessToken: tui.getToken() }, success: resolve, fail: reject }) socket.task.onOpen(() => { socket.connecting = false socket.task.send({ data: frame('CONNECT', { 'accept-version': '1.2', 'heart-beat': '10000,10000', accessToken: tui.getToken() }) }) }) socket.task.onMessage(res => handleMessage(res.data || '')) socket.task.onClose(() => { socket.connected = false socket.connecting = false clearInterval(socket.heartbeatTimer) }) socket.task.onError(() => { socket.connected = false socket.connecting = false clearInterval(socket.heartbeatTimer) }) }) } function close() { clearInterval(socket.heartbeatTimer) if (socket.task) socket.task.close() socket.connected = false socket.task = null } function resetHandlers() { socket.messageHandlers = [] socket.ackHandlers = [] socket.presenceHandlers = [] socket.roomEndHandlers = [] socket.offlineHandlers = [] } export default { connect, close, resetHandlers, subscribeRoom, sendMessage(data) { return send('/app/ie/message', data) }, sendPresence(data) { return send('/app/ie/presence', data) }, onAck(handler) { socket.ackHandlers.push(handler) }, onMessage(handler) { socket.messageHandlers.push(handler) }, onPresence(handler) { socket.presenceHandlers.push(handler) }, onRoomEnd(handler) { socket.roomEndHandlers.push(handler) }, onOffline(handler) { socket.offlineHandlers.push(handler) } }