|
@@ -9,28 +9,36 @@ import ConnectionPlugin from './ConnectionPlugin';
|
9
|
9
|
const logger = getLogger(__filename);
|
10
|
10
|
|
11
|
11
|
/**
|
12
|
|
- * Ping every 10 sec
|
|
12
|
+ * Default ping every 10 sec
|
13
|
13
|
*/
|
14
|
|
-const PING_INTERVAL = 10000;
|
|
14
|
+const PING_DEFAULT_INTERVAL = 10000;
|
15
|
15
|
|
16
|
16
|
/**
|
17
|
|
- * Ping timeout error after 5 sec of waiting.
|
|
17
|
+ * Default ping timeout error after 5 sec of waiting.
|
18
|
18
|
*/
|
19
|
|
-const PING_TIMEOUT = 5000;
|
|
19
|
+const PING_DEFAULT_TIMEOUT = 5000;
|
20
|
20
|
|
21
|
21
|
/**
|
22
|
|
- * How many ping failures will be tolerated before the WebSocket connection is killed.
|
|
22
|
+ * Default value for how many ping failures will be tolerated before the WebSocket connection is killed.
|
23
|
23
|
* The worst case scenario in case of ping timing out without a response is (25 seconds at the time of this writing):
|
24
|
24
|
* PING_THRESHOLD * PING_INTERVAL + PING_TIMEOUT
|
25
|
25
|
*/
|
26
|
|
-const PING_THRESHOLD = 2;
|
|
26
|
+const PING_DEFAULT_THRESHOLD = 2;
|
27
|
27
|
|
28
|
28
|
/**
|
29
|
|
- * The number of timestamps of send pings to keep.
|
30
|
|
- * The current value is 2 minutes.
|
31
|
|
- * @type {number} number of timestamps.
|
|
29
|
+ * How often to send ping requests.
|
32
|
30
|
*/
|
33
|
|
-const PING_TIMESTAMPS_TO_KEEP = 120000 / PING_INTERVAL;
|
|
31
|
+let pingInterval;
|
|
32
|
+
|
|
33
|
+/**
|
|
34
|
+ * The time to wait for ping responses.
|
|
35
|
+ */
|
|
36
|
+let pingTimeout;
|
|
37
|
+
|
|
38
|
+/**
|
|
39
|
+ * How many ping failures will be tolerated before the connection is killed.
|
|
40
|
+ */
|
|
41
|
+let pingThreshold;
|
34
|
42
|
|
35
|
43
|
/**
|
36
|
44
|
* XEP-0199 ping plugin.
|
|
@@ -39,17 +47,30 @@ const PING_TIMESTAMPS_TO_KEEP = 120000 / PING_INTERVAL;
|
39
|
47
|
*/
|
40
|
48
|
export default class PingConnectionPlugin extends ConnectionPlugin {
|
41
|
49
|
/**
|
42
|
|
- * Contructs new object
|
|
50
|
+ * Constructs new object
|
43
|
51
|
* @param {Object} options
|
44
|
52
|
* @param {Function} options.onPingThresholdExceeded - Callback called when ping fails too many times (controlled
|
45
|
53
|
* by the {@link PING_THRESHOLD} constant).
|
|
54
|
+ * @param {Function} options._getTimeSinceLastServerResponse - A function to obtain the last seen
|
|
55
|
+ * response from the server.
|
|
56
|
+ * @param {Object} options.pingOptions - The ping options if any.
|
46
|
57
|
* @constructor
|
47
|
58
|
*/
|
48
|
|
- constructor({ onPingThresholdExceeded }) {
|
|
59
|
+ constructor({ getTimeSinceLastServerResponse, onPingThresholdExceeded, pingOptions = {} }) {
|
49
|
60
|
super();
|
50
|
61
|
this.failedPings = 0;
|
51
|
|
- this.pingExecIntervals = new Array(PING_TIMESTAMPS_TO_KEEP);
|
52
|
62
|
this._onPingThresholdExceeded = onPingThresholdExceeded;
|
|
63
|
+ this._getTimeSinceLastServerResponse = getTimeSinceLastServerResponse;
|
|
64
|
+
|
|
65
|
+ this.pingInterval = typeof pingOptions.interval === 'number' ? pingOptions.interval : PING_DEFAULT_INTERVAL;
|
|
66
|
+ this.pingTimeout = typeof pingOptions.timeout === 'number' ? pingOptions.timeout : PING_DEFAULT_TIMEOUT;
|
|
67
|
+ this.pingThreshold = typeof pingOptions.threshold === 'number'
|
|
68
|
+ ? pingOptions.threshold : PING_DEFAULT_THRESHOLD;
|
|
69
|
+
|
|
70
|
+ // The number of timestamps of send pings to keep.
|
|
71
|
+ // The current value is 2 minutes.
|
|
72
|
+ this.pingTimestampsToKeep = Math.round(120000 / this.pingInterval);
|
|
73
|
+ this.pingExecIntervals = new Array(this.pingTimestampsToKeep);
|
53
|
74
|
}
|
54
|
75
|
|
55
|
76
|
/**
|
|
@@ -91,27 +112,45 @@ export default class PingConnectionPlugin extends ConnectionPlugin {
|
91
|
112
|
* This plugin supports only one such task and <tt>stopInterval</tt>
|
92
|
113
|
* must be called before starting a new one.
|
93
|
114
|
* @param remoteJid remote JID to which ping requests will be sent to.
|
94
|
|
- * @param interval task interval in ms.
|
95
|
115
|
*/
|
96
|
|
- startInterval(remoteJid, interval = PING_INTERVAL) {
|
|
116
|
+ startInterval(remoteJid) {
|
97
|
117
|
clearInterval(this.intervalId);
|
98
|
118
|
this.intervalId = window.setInterval(() => {
|
|
119
|
+
|
|
120
|
+ // when there were some server responses in the interval since the last time we checked (_lastServerCheck)
|
|
121
|
+ // let's skip the ping
|
|
122
|
+
|
|
123
|
+ // server response is measured on raw input and ping response time is measured after all the xmpp
|
|
124
|
+ // processing is done, and when the last server response is a ping there can be slight misalignment of the
|
|
125
|
+ // times, we give it 100ms for that processing.
|
|
126
|
+ if (this._getTimeSinceLastServerResponse() + 100 < new Date() - this._lastServerCheck) {
|
|
127
|
+ // do this just to keep in sync the intervals so we can detect suspended device
|
|
128
|
+ this._addPingExecutionTimestamp();
|
|
129
|
+
|
|
130
|
+ this._lastServerCheck = new Date();
|
|
131
|
+ this.failedPings = 0;
|
|
132
|
+
|
|
133
|
+ return;
|
|
134
|
+ }
|
|
135
|
+
|
99
|
136
|
this.ping(remoteJid, () => {
|
|
137
|
+ this._lastServerCheck = new Date();
|
|
138
|
+
|
100
|
139
|
this.failedPings = 0;
|
101
|
140
|
}, error => {
|
102
|
141
|
this.failedPings += 1;
|
103
|
142
|
const errmsg = `Ping ${error ? 'error' : 'timeout'}`;
|
104
|
143
|
|
105
|
|
- if (this.failedPings >= PING_THRESHOLD) {
|
|
144
|
+ if (this.failedPings >= pingThreshold) {
|
106
|
145
|
GlobalOnErrorHandler.callErrorHandler(new Error(errmsg));
|
107
|
146
|
logger.error(errmsg, error);
|
108
|
147
|
this._onPingThresholdExceeded && this._onPingThresholdExceeded();
|
109
|
148
|
} else {
|
110
|
149
|
logger.warn(errmsg, error);
|
111
|
150
|
}
|
112
|
|
- }, PING_TIMEOUT);
|
113
|
|
- }, interval);
|
114
|
|
- logger.info(`XMPP pings will be sent every ${interval} ms`);
|
|
151
|
+ }, pingTimeout);
|
|
152
|
+ }, this.pingInterval);
|
|
153
|
+ logger.info(`XMPP pings will be sent every ${this.pingInterval} ms`);
|
115
|
154
|
}
|
116
|
155
|
|
117
|
156
|
/**
|
|
@@ -134,7 +173,7 @@ export default class PingConnectionPlugin extends ConnectionPlugin {
|
134
|
173
|
this.pingExecIntervals.push(new Date().getTime());
|
135
|
174
|
|
136
|
175
|
// keep array length to PING_TIMESTAMPS_TO_KEEP
|
137
|
|
- if (this.pingExecIntervals.length > PING_TIMESTAMPS_TO_KEEP) {
|
|
176
|
+ if (this.pingExecIntervals.length > this.pingTimestampsToKeep) {
|
138
|
177
|
this.pingExecIntervals.shift();
|
139
|
178
|
}
|
140
|
179
|
}
|
|
@@ -172,7 +211,7 @@ export default class PingConnectionPlugin extends ConnectionPlugin {
|
172
|
211
|
// remove the interval between the ping sent
|
173
|
212
|
// this way in normal execution there is no suspend and the return
|
174
|
213
|
// will be 0 or close to 0.
|
175
|
|
- maxInterval -= PING_INTERVAL;
|
|
214
|
+ maxInterval -= pingInterval;
|
176
|
215
|
|
177
|
216
|
// make sure we do not return less than 0
|
178
|
217
|
return Math.max(maxInterval, 0);
|