@webex/internal-plugin-mercury 3.0.0-beta.9 → 3.0.0-beta.91
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -3
- package/dist/config.js +0 -7
- package/dist/config.js.map +1 -1
- package/dist/errors.js +0 -44
- package/dist/errors.js.map +1 -1
- package/dist/index.js +1 -20
- package/dist/index.js.map +1 -1
- package/dist/mercury.js +29 -148
- package/dist/mercury.js.map +1 -1
- package/dist/socket/index.js +0 -4
- package/dist/socket/index.js.map +1 -1
- package/dist/socket/socket-base.js +25 -116
- package/dist/socket/socket-base.js.map +1 -1
- package/dist/socket/socket.js +1 -7
- package/dist/socket/socket.js.map +1 -1
- package/dist/socket/socket.shim.js +2 -7
- package/dist/socket/socket.shim.js.map +1 -1
- package/package.json +14 -14
- package/src/config.js +2 -2
- package/src/errors.js +7 -5
- package/src/index.js +2 -2
- package/src/mercury.js +74 -59
- package/src/socket/socket-base.js +45 -46
- package/src/socket/socket.shim.js +6 -8
- package/test/integration/spec/mercury.js +49 -39
- package/test/integration/spec/sharable-mercury.js +19 -15
- package/test/integration/spec/webex.js +8 -7
- package/test/unit/spec/mercury-events.js +51 -60
- package/test/unit/spec/mercury.js +179 -150
- package/test/unit/spec/socket.js +246 -202
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
ConnectionError,
|
|
15
15
|
Forbidden,
|
|
16
16
|
NotAuthorized,
|
|
17
|
-
UnknownResponse
|
|
17
|
+
UnknownResponse,
|
|
18
18
|
// NotFound
|
|
19
19
|
} from '../errors';
|
|
20
20
|
|
|
@@ -88,7 +88,9 @@ export default class Socket extends EventEmitter {
|
|
|
88
88
|
* @returns {WebSocket}
|
|
89
89
|
*/
|
|
90
90
|
static getWebSocketConstructor() {
|
|
91
|
-
throw new Error(
|
|
91
|
+
throw new Error(
|
|
92
|
+
'Socket.getWebSocketConstructor() must be implemented in an environmentally appropriate way'
|
|
93
|
+
);
|
|
92
94
|
}
|
|
93
95
|
|
|
94
96
|
/**
|
|
@@ -127,18 +129,19 @@ export default class Socket extends EventEmitter {
|
|
|
127
129
|
|
|
128
130
|
options = defaults(options, {
|
|
129
131
|
code: 1000,
|
|
130
|
-
reason: 'Done'
|
|
132
|
+
reason: 'Done',
|
|
131
133
|
});
|
|
132
134
|
|
|
133
135
|
const closeTimer = safeSetTimeout(() => {
|
|
134
136
|
try {
|
|
135
137
|
this.logger.info('socket: no close event received, forcing closure');
|
|
136
|
-
resolve(
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
138
|
+
resolve(
|
|
139
|
+
this.onclose({
|
|
140
|
+
code: 1000,
|
|
141
|
+
reason: 'Done (forced)',
|
|
142
|
+
})
|
|
143
|
+
);
|
|
144
|
+
} catch (error) {
|
|
142
145
|
this.logger.warn('socket: force-close failed', error);
|
|
143
146
|
}
|
|
144
147
|
}, this.forceCloseDelay);
|
|
@@ -184,19 +187,15 @@ export default class Socket extends EventEmitter {
|
|
|
184
187
|
|
|
185
188
|
options = options || {};
|
|
186
189
|
|
|
187
|
-
checkRequired(
|
|
188
|
-
'forceCloseDelay',
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
'token',
|
|
192
|
-
'trackingId',
|
|
193
|
-
'logger'
|
|
194
|
-
], options);
|
|
190
|
+
checkRequired(
|
|
191
|
+
['forceCloseDelay', 'pingInterval', 'pongTimeout', 'token', 'trackingId', 'logger'],
|
|
192
|
+
options
|
|
193
|
+
);
|
|
195
194
|
|
|
196
195
|
Object.keys(options).forEach((key) => {
|
|
197
196
|
Reflect.defineProperty(this, key, {
|
|
198
197
|
enumerable: false,
|
|
199
|
-
value: options[key]
|
|
198
|
+
value: options[key],
|
|
200
199
|
});
|
|
201
200
|
});
|
|
202
201
|
|
|
@@ -213,10 +212,10 @@ export default class Socket extends EventEmitter {
|
|
|
213
212
|
this.logger.info('socket: closed before open', event.code, event.reason);
|
|
214
213
|
switch (event.code) {
|
|
215
214
|
case 1005:
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
215
|
+
// IE 11 doesn't seem to allow 4XXX codes, so if we get a 1005, assume
|
|
216
|
+
// it's a bad websocket url. That'll trigger a device refresh; if it
|
|
217
|
+
// turns out we had a bad token, the device refresh should 401 and
|
|
218
|
+
// trigger a token refresh.
|
|
220
219
|
return reject(new UnknownResponse(event));
|
|
221
220
|
case 4400:
|
|
222
221
|
return reject(new BadRequest(event));
|
|
@@ -224,8 +223,8 @@ export default class Socket extends EventEmitter {
|
|
|
224
223
|
return reject(new NotAuthorized(event));
|
|
225
224
|
case 4403:
|
|
226
225
|
return reject(new Forbidden(event));
|
|
227
|
-
|
|
228
|
-
|
|
226
|
+
// case 4404:
|
|
227
|
+
// return reject(new NotFound(event));
|
|
229
228
|
default:
|
|
230
229
|
return reject(new ConnectionError(event));
|
|
231
230
|
}
|
|
@@ -281,7 +280,9 @@ export default class Socket extends EventEmitter {
|
|
|
281
280
|
|
|
282
281
|
this.logger.debug('socket: sequence number: ', sequenceNumber);
|
|
283
282
|
if (this.expectedSequenceNumber && sequenceNumber !== this.expectedSequenceNumber) {
|
|
284
|
-
this.logger.debug(
|
|
283
|
+
this.logger.debug(
|
|
284
|
+
`socket: sequence number mismatch indicates lost mercury message. expected: ${this.expectedSequenceNumber}, actual: ${sequenceNumber}`
|
|
285
|
+
);
|
|
285
286
|
this.emit('sequence-mismatch', sequenceNumber, this.expectedSequenceNumber);
|
|
286
287
|
}
|
|
287
288
|
this.expectedSequenceNumber = sequenceNumber + 1;
|
|
@@ -294,12 +295,10 @@ export default class Socket extends EventEmitter {
|
|
|
294
295
|
this._acknowledge(processedEvent);
|
|
295
296
|
if (data.type === 'pong') {
|
|
296
297
|
this.emit('pong', processedEvent);
|
|
297
|
-
}
|
|
298
|
-
else {
|
|
298
|
+
} else {
|
|
299
299
|
this.emit('message', processedEvent);
|
|
300
300
|
}
|
|
301
|
-
}
|
|
302
|
-
catch (error) {
|
|
301
|
+
} catch (error) {
|
|
303
302
|
// The above code should only be able to throw if we receive an unparsable
|
|
304
303
|
// message from Mercury. At this time, the only action we have is to
|
|
305
304
|
// ignore it and move on.
|
|
@@ -347,7 +346,7 @@ export default class Socket extends EventEmitter {
|
|
|
347
346
|
|
|
348
347
|
return this.send({
|
|
349
348
|
messageId: event.data.id,
|
|
350
|
-
type: 'ack'
|
|
349
|
+
type: 'ack',
|
|
351
350
|
});
|
|
352
351
|
}
|
|
353
352
|
|
|
@@ -363,14 +362,18 @@ export default class Socket extends EventEmitter {
|
|
|
363
362
|
id: uuid.v4(),
|
|
364
363
|
type: 'authorization',
|
|
365
364
|
data: {
|
|
366
|
-
token: this.token
|
|
365
|
+
token: this.token,
|
|
367
366
|
},
|
|
368
367
|
trackingId: this.trackingId,
|
|
369
|
-
logLevelToken: this.logLevelToken
|
|
368
|
+
logLevelToken: this.logLevelToken,
|
|
370
369
|
});
|
|
371
370
|
|
|
372
371
|
const waitForBufferState = (event) => {
|
|
373
|
-
if (
|
|
372
|
+
if (
|
|
373
|
+
!event.data.type &&
|
|
374
|
+
(event.data.data.eventType === 'mercury.buffer_state' ||
|
|
375
|
+
event.data.data.eventType === 'mercury.registration_status')
|
|
376
|
+
) {
|
|
374
377
|
this.removeListener('message', waitForBufferState);
|
|
375
378
|
this._ping();
|
|
376
379
|
resolve();
|
|
@@ -423,11 +426,10 @@ export default class Socket extends EventEmitter {
|
|
|
423
426
|
this.logger.debug('socket: expected', id, 'received', event.data.id);
|
|
424
427
|
this.close({
|
|
425
428
|
code: 1000,
|
|
426
|
-
reason: 'Pong mismatch'
|
|
429
|
+
reason: 'Pong mismatch',
|
|
427
430
|
});
|
|
428
431
|
}
|
|
429
|
-
}
|
|
430
|
-
catch (error) {
|
|
432
|
+
} catch (error) {
|
|
431
433
|
// This try/catch block was added as a debugging step; to the best of my
|
|
432
434
|
// knowledge, the above can never throw.
|
|
433
435
|
/* istanbul ignore next */
|
|
@@ -440,13 +442,11 @@ export default class Socket extends EventEmitter {
|
|
|
440
442
|
this.logger.info('socket: pong not receive in expected period, closing socket');
|
|
441
443
|
this.close({
|
|
442
444
|
code: 1000,
|
|
443
|
-
reason: 'Pong not received'
|
|
444
|
-
})
|
|
445
|
-
.
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
}
|
|
449
|
-
catch (error) {
|
|
445
|
+
reason: 'Pong not received',
|
|
446
|
+
}).catch((reason) => {
|
|
447
|
+
this.logger.warn('socket: failed to close socket after missed pong', reason);
|
|
448
|
+
});
|
|
449
|
+
} catch (error) {
|
|
450
450
|
// This try/catch block was added as a debugging step; to the best of my
|
|
451
451
|
// knowledge, the above can never throw.
|
|
452
452
|
/* istanbul ignore next */
|
|
@@ -458,8 +458,7 @@ export default class Socket extends EventEmitter {
|
|
|
458
458
|
try {
|
|
459
459
|
clearTimeout(this.pongTimer);
|
|
460
460
|
this.pingTimer = safeSetTimeout(() => this._ping(), this.pingInterval);
|
|
461
|
-
}
|
|
462
|
-
catch (error) {
|
|
461
|
+
} catch (error) {
|
|
463
462
|
// This try/catch block was added as a debugging step; to the best of my
|
|
464
463
|
// knowledge, the above can never throw.
|
|
465
464
|
/* istanbul ignore next */
|
|
@@ -476,7 +475,7 @@ export default class Socket extends EventEmitter {
|
|
|
476
475
|
|
|
477
476
|
return this.send({
|
|
478
477
|
id,
|
|
479
|
-
type: 'ping'
|
|
478
|
+
type: 'ping',
|
|
480
479
|
});
|
|
481
480
|
}
|
|
482
481
|
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
/* eslint-disable no-restricted-globals */
|
|
2
|
+
|
|
1
3
|
/*!
|
|
2
4
|
* Copyright (c) 2015-2020 Cisco Systems, Inc. See LICENSE file.
|
|
3
5
|
*/
|
|
@@ -12,18 +14,14 @@ Socket.getWebSocketConstructor = function getWebSocketConstructor() {
|
|
|
12
14
|
|
|
13
15
|
if (typeof WebSocket !== 'undefined') {
|
|
14
16
|
ws = WebSocket;
|
|
15
|
-
}
|
|
16
|
-
else if (typeof MozWebSocket !== 'undefined') {
|
|
17
|
+
} else if (typeof MozWebSocket !== 'undefined') {
|
|
17
18
|
// eslint-disable-next-line no-undef
|
|
18
19
|
ws = MozWebSocket;
|
|
19
|
-
}
|
|
20
|
-
else if (typeof global !== 'undefined') {
|
|
20
|
+
} else if (typeof global !== 'undefined') {
|
|
21
21
|
ws = global.WebSocket || global.MozWebSocket;
|
|
22
|
-
}
|
|
23
|
-
else if (typeof window !== 'undefined') {
|
|
22
|
+
} else if (typeof window !== 'undefined') {
|
|
24
23
|
ws = window.WebSocket || window.MozWebSocket;
|
|
25
|
-
}
|
|
26
|
-
else if (typeof self !== 'undefined') {
|
|
24
|
+
} else if (typeof self !== 'undefined') {
|
|
27
25
|
ws = self.WebSocket || self.MozWebSocket;
|
|
28
26
|
}
|
|
29
27
|
|
|
@@ -16,34 +16,37 @@ describe('plugin-mercury', function () {
|
|
|
16
16
|
describe('Mercury', () => {
|
|
17
17
|
let webex;
|
|
18
18
|
|
|
19
|
-
beforeEach(() =>
|
|
20
|
-
.then((users) => {
|
|
19
|
+
beforeEach(() =>
|
|
20
|
+
testUsers.create({count: 1}).then((users) => {
|
|
21
21
|
webex = new WebexCore({
|
|
22
22
|
credentials: {
|
|
23
|
-
supertoken: users[0].token
|
|
23
|
+
supertoken: users[0].token,
|
|
24
24
|
},
|
|
25
25
|
config: {
|
|
26
26
|
credentials: {
|
|
27
|
-
refreshCallback
|
|
28
|
-
}
|
|
29
|
-
}
|
|
27
|
+
refreshCallback,
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
30
|
});
|
|
31
|
-
})
|
|
31
|
+
})
|
|
32
|
+
);
|
|
32
33
|
|
|
33
34
|
afterEach(() => webex && webex.internal.mercury.disconnect());
|
|
34
35
|
|
|
35
36
|
describe('#connect()', () => {
|
|
36
37
|
it('connects to mercury', () => webex.internal.mercury.connect());
|
|
37
38
|
|
|
38
|
-
it('refreshes the access token when a 4401 is received', () =>
|
|
39
|
-
.
|
|
40
|
-
|
|
41
|
-
|
|
39
|
+
it('refreshes the access token when a 4401 is received', () =>
|
|
40
|
+
webex.internal.device
|
|
41
|
+
.register()
|
|
42
|
+
.then(() => {
|
|
43
|
+
// eslint-disable-next-line camelcase
|
|
44
|
+
webex.credentials.supertoken.access_token = 'fake token';
|
|
42
45
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
46
|
+
return webex.internal.mercury.connect();
|
|
47
|
+
})
|
|
48
|
+
// eslint-disable-next-line camelcase
|
|
49
|
+
.then(() => assert.notEqual(webex.credentials.supertoken.access_token, 'fake token')));
|
|
47
50
|
|
|
48
51
|
// This doesn't work as designed yet. The only way to get a 4404 is to try
|
|
49
52
|
// to connect to someone else's valid registration; the intent was to get
|
|
@@ -70,26 +73,34 @@ describe('plugin-mercury', function () {
|
|
|
70
73
|
});
|
|
71
74
|
|
|
72
75
|
describe('when web-high-availability is enabled', () => {
|
|
73
|
-
flaky(it, process.env.SKIP_FLAKY_TESTS)(
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
76
|
+
flaky(it, process.env.SKIP_FLAKY_TESTS)(
|
|
77
|
+
'connects to mercury using service catalog url',
|
|
78
|
+
() => {
|
|
79
|
+
let defaultWebSocketUrl;
|
|
80
|
+
|
|
81
|
+
// we need to ensure the feature is set for user before "registering"
|
|
82
|
+
// the device
|
|
83
|
+
return (
|
|
84
|
+
webex.internal.device
|
|
85
|
+
.register()
|
|
86
|
+
.then(() =>
|
|
87
|
+
webex.internal.feature.setFeature('developer', 'web-high-availability', true)
|
|
88
|
+
)
|
|
89
|
+
.then(() => webex.internal.device.unregister())
|
|
90
|
+
// start the test flow the device list
|
|
91
|
+
.then(() => webex.internal.device.register())
|
|
92
|
+
.then(() => {
|
|
93
|
+
defaultWebSocketUrl = webex.internal.device.webSocketUrl;
|
|
94
|
+
})
|
|
95
|
+
.then(() => webex.internal.mercury.connect())
|
|
96
|
+
.then(() => webex.internal.device.getWebSocketUrl())
|
|
97
|
+
.then((wsUrl) => {
|
|
98
|
+
assert.notEqual(defaultWebSocketUrl, webex.internal.mercury.socket.url);
|
|
99
|
+
assert.include(webex.internal.mercury.socket.url, wsUrl);
|
|
100
|
+
})
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
);
|
|
93
104
|
});
|
|
94
105
|
});
|
|
95
106
|
|
|
@@ -98,10 +109,9 @@ describe('plugin-mercury', function () {
|
|
|
98
109
|
|
|
99
110
|
webex.internal.mercury.on('event:mercury.buffer_state', spy);
|
|
100
111
|
|
|
101
|
-
return webex.internal.mercury.connect()
|
|
102
|
-
.
|
|
103
|
-
|
|
104
|
-
});
|
|
112
|
+
return webex.internal.mercury.connect().then(() => {
|
|
113
|
+
assert.calledOnce(spy);
|
|
114
|
+
});
|
|
105
115
|
});
|
|
106
116
|
});
|
|
107
117
|
});
|
|
@@ -14,17 +14,19 @@ describe('plugin-mercury', function () {
|
|
|
14
14
|
describe('Sharable Mercury', () => {
|
|
15
15
|
let webex;
|
|
16
16
|
|
|
17
|
-
beforeEach(() =>
|
|
18
|
-
.then((users) => {
|
|
17
|
+
beforeEach(() =>
|
|
18
|
+
testUsers.create({count: 1}).then((users) => {
|
|
19
19
|
webex = new WebexCore({
|
|
20
20
|
credentials: {
|
|
21
|
-
supertoken: users[0].token
|
|
22
|
-
}
|
|
21
|
+
supertoken: users[0].token,
|
|
22
|
+
},
|
|
23
23
|
});
|
|
24
24
|
|
|
25
|
-
return webex.internal.device
|
|
25
|
+
return webex.internal.device
|
|
26
|
+
.register()
|
|
26
27
|
.then(() => webex.internal.feature.setFeature('developer', 'web-shared-mercury', true));
|
|
27
|
-
})
|
|
28
|
+
})
|
|
29
|
+
);
|
|
28
30
|
|
|
29
31
|
afterEach(() => webex && webex.internal.mercury.disconnect());
|
|
30
32
|
|
|
@@ -39,17 +41,19 @@ describe('plugin-mercury', function () {
|
|
|
39
41
|
webex.internal.mercury.on('event:mercury.buffer_state', spy1);
|
|
40
42
|
webex.internal.mercury.on('event:mercury.registration_status', spy2);
|
|
41
43
|
|
|
42
|
-
return webex.internal.mercury.connect()
|
|
43
|
-
.
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
const {data} = spy2.args[0][0];
|
|
44
|
+
return webex.internal.mercury.connect().then(() => {
|
|
45
|
+
assert.notCalled(spy1);
|
|
46
|
+
assert.calledOnce(spy2);
|
|
47
|
+
const {data} = spy2.args[0][0];
|
|
47
48
|
|
|
48
|
-
|
|
49
|
-
|
|
49
|
+
assert.property(data, 'bufferState');
|
|
50
|
+
assert.property(data, 'localClusterServiceUrls');
|
|
50
51
|
|
|
51
|
-
|
|
52
|
-
|
|
52
|
+
assert.deepEqual(
|
|
53
|
+
webex.internal.mercury.localClusterServiceUrls,
|
|
54
|
+
data.localClusterServiceUrls
|
|
55
|
+
);
|
|
56
|
+
});
|
|
53
57
|
});
|
|
54
58
|
});
|
|
55
59
|
});
|
|
@@ -14,25 +14,26 @@ describe('plugin-mercury', function () {
|
|
|
14
14
|
|
|
15
15
|
let webex;
|
|
16
16
|
|
|
17
|
-
beforeEach('create users', () =>
|
|
18
|
-
.then(async (users) => {
|
|
17
|
+
beforeEach('create users', () =>
|
|
18
|
+
testUsers.create({count: 1}).then(async (users) => {
|
|
19
19
|
// Pause for 5 seconds for CI
|
|
20
20
|
await new Promise((done) => setTimeout(done, 5000));
|
|
21
21
|
|
|
22
22
|
webex = new WebexCore({
|
|
23
23
|
credentials: {
|
|
24
|
-
supertoken: users[0].token
|
|
25
|
-
}
|
|
24
|
+
supertoken: users[0].token,
|
|
25
|
+
},
|
|
26
26
|
});
|
|
27
27
|
sinon.spy(webex.internal.mercury, 'disconnect');
|
|
28
28
|
sinon.spy(webex.internal.device, 'unregister');
|
|
29
29
|
|
|
30
30
|
return webex.internal.mercury.connect();
|
|
31
|
-
})
|
|
31
|
+
})
|
|
32
|
+
);
|
|
32
33
|
|
|
33
34
|
describe('onBeforeLogout()', () => {
|
|
34
|
-
it('disconnects the web socket', () =>
|
|
35
|
-
.then(() => {
|
|
35
|
+
it('disconnects the web socket', () =>
|
|
36
|
+
webex.logout({noRedirect: true}).then(() => {
|
|
36
37
|
assert.called(webex.internal.mercury.disconnect);
|
|
37
38
|
assert.isFalse(webex.internal.mercury.connected);
|
|
38
39
|
assert.called(webex.internal.device.unregister);
|