GramJs: Wake-up pings for faster reconnect after idle mode
This commit is contained in:
parent
a55fbdfcc4
commit
1c0388f6d1
@ -93,7 +93,9 @@ export async function init(_onUpdate: OnApiUpdate, initialArgs: ApiInitialArgs)
|
|||||||
initialMethod: platform === 'iOS' || platform === 'Android' ? 'phoneNumber' : 'qrCode',
|
initialMethod: platform === 'iOS' || platform === 'Android' ? 'phoneNumber' : 'qrCode',
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
// TODO Investigate which request causes this exception
|
// eslint-disable-next-line no-console
|
||||||
|
console.error(err);
|
||||||
|
|
||||||
if (err.message !== 'Disconnect') {
|
if (err.message !== 'Disconnect') {
|
||||||
onUpdate({
|
onUpdate({
|
||||||
'@type': 'updateConnectionState',
|
'@type': 'updateConnectionState',
|
||||||
|
|||||||
@ -11,8 +11,11 @@ const {
|
|||||||
constructors,
|
constructors,
|
||||||
requests,
|
requests,
|
||||||
} = require('../tl');
|
} = require('../tl');
|
||||||
const MTProtoSender = require('../network/MTProtoSender');
|
const {
|
||||||
const { ConnectionTCPObfuscated } = require('../network/connection/TCPObfuscated');
|
ConnectionTCPObfuscated,
|
||||||
|
MTProtoSender,
|
||||||
|
UpdateConnectionState,
|
||||||
|
} = require('../network');
|
||||||
const {
|
const {
|
||||||
authFlow,
|
authFlow,
|
||||||
checkAuthorization,
|
checkAuthorization,
|
||||||
@ -33,6 +36,14 @@ const PING_INTERVAL = 3000; // 3 sec
|
|||||||
const PING_TIMEOUT = 5000; // 5 sec
|
const PING_TIMEOUT = 5000; // 5 sec
|
||||||
const PING_FAIL_ATTEMPTS = 3;
|
const PING_FAIL_ATTEMPTS = 3;
|
||||||
const PING_FAIL_INTERVAL = 100; // ms
|
const PING_FAIL_INTERVAL = 100; // ms
|
||||||
|
|
||||||
|
// An unusually long interval is a sign of returning from background mode...
|
||||||
|
const PING_INTERVAL_TO_WAKE_UP = 5000; // 5 sec
|
||||||
|
// ... so we send a quick "wake-up" ping to confirm than connection was dropped ASAP
|
||||||
|
const PING_WAKE_UP_TIMEOUT = 3000; // 3 sec
|
||||||
|
// We also send a warning to the user even a bit more quickly
|
||||||
|
const PING_WAKE_UP_WARNING_TIMEOUT = 1000; // 1 sec
|
||||||
|
|
||||||
const PING_DISCONNECT_DELAY = 60000; // 1 min
|
const PING_DISCONNECT_DELAY = 60000; // 1 min
|
||||||
|
|
||||||
// All types
|
// All types
|
||||||
@ -215,23 +226,51 @@ class TelegramClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async _updateLoop() {
|
async _updateLoop() {
|
||||||
|
let lastPongAt;
|
||||||
|
|
||||||
while (!this._destroyed) {
|
while (!this._destroyed) {
|
||||||
await Helpers.sleep(PING_INTERVAL);
|
await Helpers.sleep(PING_INTERVAL);
|
||||||
if (this._sender.isReconnecting || this._isSwitchingDc) {
|
if (this._sender.isReconnecting || this._isSwitchingDc) {
|
||||||
|
lastPongAt = undefined;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await attempts(() => {
|
const ping = () => {
|
||||||
return timeout(this._sender.send(new requests.PingDelayDisconnect({
|
return this._sender.send(new requests.PingDelayDisconnect({
|
||||||
pingId: Helpers.getRandomInt(Number.MIN_SAFE_INTEGER, Number.MAX_SAFE_INTEGER),
|
pingId: Helpers.getRandomInt(Number.MIN_SAFE_INTEGER, Number.MAX_SAFE_INTEGER),
|
||||||
disconnectDelay: PING_DISCONNECT_DELAY,
|
disconnectDelay: PING_DISCONNECT_DELAY,
|
||||||
})), PING_TIMEOUT);
|
}));
|
||||||
}, PING_FAIL_ATTEMPTS, PING_FAIL_INTERVAL);
|
};
|
||||||
|
|
||||||
|
const pingAt = Date.now();
|
||||||
|
const lastInterval = lastPongAt ? pingAt - lastPongAt : undefined;
|
||||||
|
|
||||||
|
if (!lastInterval || lastInterval < PING_INTERVAL_TO_WAKE_UP) {
|
||||||
|
await attempts(() => timeout(ping, PING_TIMEOUT), PING_FAIL_ATTEMPTS, PING_FAIL_INTERVAL);
|
||||||
|
} else {
|
||||||
|
let wakeUpWarningTimeout = setTimeout(() => {
|
||||||
|
this._handleUpdate(new UpdateConnectionState(UpdateConnectionState.disconnected));
|
||||||
|
wakeUpWarningTimeout = undefined;
|
||||||
|
}, PING_WAKE_UP_WARNING_TIMEOUT);
|
||||||
|
|
||||||
|
await timeout(ping, PING_WAKE_UP_TIMEOUT);
|
||||||
|
|
||||||
|
if (wakeUpWarningTimeout) {
|
||||||
|
clearTimeout(wakeUpWarningTimeout);
|
||||||
|
wakeUpWarningTimeout = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._handleUpdate(new UpdateConnectionState(UpdateConnectionState.connected));
|
||||||
|
}
|
||||||
|
|
||||||
|
lastPongAt = Date.now();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
console.warn(err);
|
console.warn(err);
|
||||||
|
|
||||||
|
lastPongAt = undefined;
|
||||||
|
|
||||||
if (this._sender.isReconnecting || this._isSwitchingDc) {
|
if (this._sender.isReconnecting || this._isSwitchingDc) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -251,6 +290,8 @@ class TelegramClient {
|
|||||||
} catch (e) {
|
} catch (e) {
|
||||||
// we don't care about errors here
|
// we don't care about errors here
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lastPongAt = undefined;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
await this.disconnect();
|
await this.disconnect();
|
||||||
@ -1052,9 +1093,9 @@ class TelegramClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function timeout(promise, ms) {
|
function timeout(cb, ms) {
|
||||||
return Promise.race([
|
return Promise.race([
|
||||||
promise,
|
cb(),
|
||||||
Helpers.sleep(ms)
|
Helpers.sleep(ms)
|
||||||
.then(() => Promise.reject(new Error('TIMEOUT'))),
|
.then(() => Promise.reject(new Error('TIMEOUT'))),
|
||||||
]);
|
]);
|
||||||
|
|||||||
@ -21,7 +21,7 @@ const BinaryReader = require('../extensions/BinaryReader');
|
|||||||
const {
|
const {
|
||||||
UpdateConnectionState,
|
UpdateConnectionState,
|
||||||
UpdateServerTimeOffset,
|
UpdateServerTimeOffset,
|
||||||
} = require('./index');
|
} = require('./updates');
|
||||||
const { BadMessageError } = require('../errors/Common');
|
const { BadMessageError } = require('../errors/Common');
|
||||||
const {
|
const {
|
||||||
BadServerSalt,
|
BadServerSalt,
|
||||||
@ -169,6 +169,8 @@ class MTProtoSender {
|
|||||||
* @returns {Promise<boolean>}
|
* @returns {Promise<boolean>}
|
||||||
*/
|
*/
|
||||||
async connect(connection, force) {
|
async connect(connection, force) {
|
||||||
|
this.userDisconnected = false;
|
||||||
|
|
||||||
if (this._user_connected && !force) {
|
if (this._user_connected && !force) {
|
||||||
this._log.info('User is already connected!');
|
this._log.info('User is already connected!');
|
||||||
return false;
|
return false;
|
||||||
@ -315,13 +317,15 @@ class MTProtoSender {
|
|||||||
async _disconnect() {
|
async _disconnect() {
|
||||||
this._send_queue.rejectAll();
|
this._send_queue.rejectAll();
|
||||||
|
|
||||||
|
if (this._updateCallback) {
|
||||||
|
this._updateCallback(new UpdateConnectionState(UpdateConnectionState.disconnected));
|
||||||
|
}
|
||||||
|
|
||||||
if (this._connection === undefined) {
|
if (this._connection === undefined) {
|
||||||
this._log.info('Not disconnecting (already have no connection)');
|
this._log.info('Not disconnecting (already have no connection)');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (this._updateCallback) {
|
|
||||||
this._updateCallback(new UpdateConnectionState(UpdateConnectionState.disconnected));
|
|
||||||
}
|
|
||||||
this._log.info('Disconnecting from %s...'.replace('%s', this._connection.toString()));
|
this._log.info('Disconnecting from %s...'.replace('%s', this._connection.toString()));
|
||||||
this._user_connected = false;
|
this._user_connected = false;
|
||||||
this._log.debug('Closing current connection...');
|
this._log.debug('Closing current connection...');
|
||||||
|
|||||||
@ -1,24 +1,6 @@
|
|||||||
const MTProtoPlainSender = require('./MTProtoPlainSender');
|
const MTProtoPlainSender = require('./MTProtoPlainSender');
|
||||||
const MTProtoSender = require('./MTProtoSender');
|
const MTProtoSender = require('./MTProtoSender');
|
||||||
|
|
||||||
class UpdateConnectionState {
|
|
||||||
static disconnected = -1;
|
|
||||||
|
|
||||||
static connected = 1;
|
|
||||||
|
|
||||||
static broken = 0;
|
|
||||||
|
|
||||||
constructor(state) {
|
|
||||||
this.state = state;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class UpdateServerTimeOffset {
|
|
||||||
constructor(timeOffset) {
|
|
||||||
this.timeOffset = timeOffset;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
Connection,
|
Connection,
|
||||||
ConnectionTCPFull,
|
ConnectionTCPFull,
|
||||||
@ -26,6 +8,11 @@ const {
|
|||||||
ConnectionTCPObfuscated,
|
ConnectionTCPObfuscated,
|
||||||
} = require('./connection');
|
} = require('./connection');
|
||||||
|
|
||||||
|
const {
|
||||||
|
UpdateConnectionState,
|
||||||
|
UpdateServerTimeOffset,
|
||||||
|
} = require('./updates');
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
Connection,
|
Connection,
|
||||||
ConnectionTCPFull,
|
ConnectionTCPFull,
|
||||||
|
|||||||
23
src/lib/gramjs/network/updates.js
Normal file
23
src/lib/gramjs/network/updates.js
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
class UpdateConnectionState {
|
||||||
|
static disconnected = -1;
|
||||||
|
|
||||||
|
static connected = 1;
|
||||||
|
|
||||||
|
static broken = 0;
|
||||||
|
|
||||||
|
constructor(state, origin) {
|
||||||
|
this.state = state;
|
||||||
|
this.origin = origin;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class UpdateServerTimeOffset {
|
||||||
|
constructor(timeOffset) {
|
||||||
|
this.timeOffset = timeOffset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
UpdateConnectionState,
|
||||||
|
UpdateServerTimeOffset,
|
||||||
|
};
|
||||||
@ -47,14 +47,28 @@ addReducer('afterSync', () => {
|
|||||||
void afterSync();
|
void afterSync();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const RELEASE_STATUS_TIMEOUT = 15000; // 10 sec;
|
||||||
|
|
||||||
|
let releaseStatusTimeout: number | undefined;
|
||||||
|
|
||||||
async function sync(afterSyncCallback: () => void) {
|
async function sync(afterSyncCallback: () => void) {
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
console.log('>>> START SYNC');
|
console.log('>>> START SYNC');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (releaseStatusTimeout) {
|
||||||
|
clearTimeout(releaseStatusTimeout);
|
||||||
|
}
|
||||||
|
|
||||||
setGlobal({ ...getGlobal(), isSyncing: true });
|
setGlobal({ ...getGlobal(), isSyncing: true });
|
||||||
|
|
||||||
|
// Workaround for `isSyncing = true` sometimes getting stuck for some reason
|
||||||
|
releaseStatusTimeout = window.setTimeout(() => {
|
||||||
|
setGlobal({ ...getGlobal(), isSyncing: false });
|
||||||
|
releaseStatusTimeout = undefined;
|
||||||
|
}, RELEASE_STATUS_TIMEOUT);
|
||||||
|
|
||||||
await callApi('fetchCurrentUser');
|
await callApi('fetchCurrentUser');
|
||||||
|
|
||||||
// This fetches only active chats and clears archived chats, which will be fetched in `afterSync`
|
// This fetches only active chats and clears archived chats, which will be fetched in `afterSync`
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user