UNPKG

3.69 kBJavaScriptView Raw
1import d from 'debug';
2import WebSocketPlus, {
3 OPEN,
4 DISCONNECT,
5 RECONNECT,
6 RETRY,
7 SCHEDULE,
8 OFFLINE,
9 ONLINE,
10 ERROR,
11 MESSAGE,
12} from './websocket-plus';
13import { createError } from './error';
14import { GenericCommand, CommandType } from '../proto/message';
15import { trim, isWeapp } from './utils';
16
17const debug = d('LC:Connection');
18
19const COMMAND_TIMEOUT = 20000;
20
21const EXPIRE = Symbol('expire');
22
23export {
24 OPEN,
25 DISCONNECT,
26 RECONNECT,
27 RETRY,
28 SCHEDULE,
29 OFFLINE,
30 ONLINE,
31 ERROR,
32 MESSAGE,
33 EXPIRE,
34};
35
36export default class Connection extends WebSocketPlus {
37 constructor(getUrl, { format, version }) {
38 debug('initializing Connection');
39 const protocolString = `lc.${format}.${version}`;
40 if (!isWeapp) {
41 super(getUrl, protocolString);
42 } else {
43 super(
44 getUrl().then(urls =>
45 urls.map(
46 url =>
47 `${url}${
48 url.indexOf('?') === -1 ? '?' : '&'
49 }subprotocol=${encodeURIComponent(protocolString)}`
50 )
51 )
52 );
53 }
54 this._protocalFormat = format;
55 this._commands = {};
56 this._serialId = 0;
57 }
58
59 async send(command, waitingForRespond = true) {
60 let serialId;
61 if (waitingForRespond) {
62 this._serialId += 1;
63 serialId = this._serialId;
64 command.i = serialId; // eslint-disable-line no-param-reassign
65 }
66 if (debug.enabled) debug('↑ %O sent', trim(command));
67
68 let message;
69 if (this._protocalFormat === 'proto2base64') {
70 message = command.toBase64();
71 } else if (command.toArrayBuffer) {
72 message = command.toArrayBuffer();
73 }
74 if (!message) {
75 throw new TypeError(`${command} is not a GenericCommand`);
76 }
77
78 super.send(message);
79
80 if (!waitingForRespond) return undefined;
81 return new Promise((resolve, reject) => {
82 this._commands[serialId] = {
83 resolve,
84 reject,
85 timeout: setTimeout(() => {
86 if (this._commands[serialId]) {
87 if (debug.enabled) debug('✗ %O timeout', trim(command));
88 reject(
89 createError({
90 error: `Command Timeout [cmd:${command.cmd} op:${command.op}]`,
91 name: 'COMMAND_TIMEOUT',
92 })
93 );
94 delete this._commands[serialId];
95 }
96 }, COMMAND_TIMEOUT),
97 };
98 });
99 }
100
101 handleMessage(msg) {
102 let message;
103 try {
104 message = GenericCommand.decode(msg);
105 if (debug.enabled) debug('↓ %O received', trim(message));
106 } catch (e) {
107 console.warn('Decode message failed:', e.message, msg);
108 return;
109 }
110 const serialId = message.i;
111 if (serialId) {
112 if (this._commands[serialId]) {
113 clearTimeout(this._commands[serialId].timeout);
114 if (message.cmd === CommandType.error) {
115 this._commands[serialId].reject(createError(message.errorMessage));
116 } else {
117 this._commands[serialId].resolve(message);
118 }
119 delete this._commands[serialId];
120 } else {
121 console.warn(`Unexpected command received with serialId [${serialId}],
122 which have timed out or never been requested.`);
123 }
124 } else {
125 switch (message.cmd) {
126 case CommandType.error: {
127 this.emit(ERROR, createError(message.errorMessage));
128 return;
129 }
130 case CommandType.goaway: {
131 this.emit(EXPIRE);
132 return;
133 }
134 default: {
135 this.emit(MESSAGE, message);
136 }
137 }
138 }
139 }
140
141 ping() {
142 return this.send(
143 new GenericCommand({
144 cmd: CommandType.echo,
145 })
146 ).catch(error => debug('ping failed:', error));
147 }
148}