mirror of
https://github.com/danog/telegram-tt.git
synced 2025-01-21 21:01:29 +01:00
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',
|
||||
});
|
||||
} catch (err) {
|
||||
// TODO Investigate which request causes this exception
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(err);
|
||||
|
||||
if (err.message !== 'Disconnect') {
|
||||
onUpdate({
|
||||
'@type': 'updateConnectionState',
|
||||
|
@ -11,8 +11,11 @@ const {
|
||||
constructors,
|
||||
requests,
|
||||
} = require('../tl');
|
||||
const MTProtoSender = require('../network/MTProtoSender');
|
||||
const { ConnectionTCPObfuscated } = require('../network/connection/TCPObfuscated');
|
||||
const {
|
||||
ConnectionTCPObfuscated,
|
||||
MTProtoSender,
|
||||
UpdateConnectionState,
|
||||
} = require('../network');
|
||||
const {
|
||||
authFlow,
|
||||
checkAuthorization,
|
||||
@ -33,6 +36,14 @@ const PING_INTERVAL = 3000; // 3 sec
|
||||
const PING_TIMEOUT = 5000; // 5 sec
|
||||
const PING_FAIL_ATTEMPTS = 3;
|
||||
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
|
||||
|
||||
// All types
|
||||
@ -215,23 +226,51 @@ class TelegramClient {
|
||||
}
|
||||
|
||||
async _updateLoop() {
|
||||
let lastPongAt;
|
||||
|
||||
while (!this._destroyed) {
|
||||
await Helpers.sleep(PING_INTERVAL);
|
||||
if (this._sender.isReconnecting || this._isSwitchingDc) {
|
||||
lastPongAt = undefined;
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
await attempts(() => {
|
||||
return timeout(this._sender.send(new requests.PingDelayDisconnect({
|
||||
const ping = () => {
|
||||
return this._sender.send(new requests.PingDelayDisconnect({
|
||||
pingId: Helpers.getRandomInt(Number.MIN_SAFE_INTEGER, Number.MAX_SAFE_INTEGER),
|
||||
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) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn(err);
|
||||
|
||||
lastPongAt = undefined;
|
||||
|
||||
if (this._sender.isReconnecting || this._isSwitchingDc) {
|
||||
continue;
|
||||
}
|
||||
@ -251,6 +290,8 @@ class TelegramClient {
|
||||
} catch (e) {
|
||||
// we don't care about errors here
|
||||
}
|
||||
|
||||
lastPongAt = undefined;
|
||||
}
|
||||
}
|
||||
await this.disconnect();
|
||||
@ -1052,9 +1093,9 @@ class TelegramClient {
|
||||
}
|
||||
}
|
||||
|
||||
function timeout(promise, ms) {
|
||||
function timeout(cb, ms) {
|
||||
return Promise.race([
|
||||
promise,
|
||||
cb(),
|
||||
Helpers.sleep(ms)
|
||||
.then(() => Promise.reject(new Error('TIMEOUT'))),
|
||||
]);
|
||||
|
@ -21,7 +21,7 @@ const BinaryReader = require('../extensions/BinaryReader');
|
||||
const {
|
||||
UpdateConnectionState,
|
||||
UpdateServerTimeOffset,
|
||||
} = require('./index');
|
||||
} = require('./updates');
|
||||
const { BadMessageError } = require('../errors/Common');
|
||||
const {
|
||||
BadServerSalt,
|
||||
@ -169,6 +169,8 @@ class MTProtoSender {
|
||||
* @returns {Promise<boolean>}
|
||||
*/
|
||||
async connect(connection, force) {
|
||||
this.userDisconnected = false;
|
||||
|
||||
if (this._user_connected && !force) {
|
||||
this._log.info('User is already connected!');
|
||||
return false;
|
||||
@ -315,13 +317,15 @@ class MTProtoSender {
|
||||
async _disconnect() {
|
||||
this._send_queue.rejectAll();
|
||||
|
||||
if (this._updateCallback) {
|
||||
this._updateCallback(new UpdateConnectionState(UpdateConnectionState.disconnected));
|
||||
}
|
||||
|
||||
if (this._connection === undefined) {
|
||||
this._log.info('Not disconnecting (already have no connection)');
|
||||
return;
|
||||
}
|
||||
if (this._updateCallback) {
|
||||
this._updateCallback(new UpdateConnectionState(UpdateConnectionState.disconnected));
|
||||
}
|
||||
|
||||
this._log.info('Disconnecting from %s...'.replace('%s', this._connection.toString()));
|
||||
this._user_connected = false;
|
||||
this._log.debug('Closing current connection...');
|
||||
|
@ -1,24 +1,6 @@
|
||||
const MTProtoPlainSender = require('./MTProtoPlainSender');
|
||||
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 {
|
||||
Connection,
|
||||
ConnectionTCPFull,
|
||||
@ -26,6 +8,11 @@ const {
|
||||
ConnectionTCPObfuscated,
|
||||
} = require('./connection');
|
||||
|
||||
const {
|
||||
UpdateConnectionState,
|
||||
UpdateServerTimeOffset,
|
||||
} = require('./updates');
|
||||
|
||||
module.exports = {
|
||||
Connection,
|
||||
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();
|
||||
});
|
||||
|
||||
const RELEASE_STATUS_TIMEOUT = 15000; // 10 sec;
|
||||
|
||||
let releaseStatusTimeout: number | undefined;
|
||||
|
||||
async function sync(afterSyncCallback: () => void) {
|
||||
if (DEBUG) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('>>> START SYNC');
|
||||
}
|
||||
|
||||
if (releaseStatusTimeout) {
|
||||
clearTimeout(releaseStatusTimeout);
|
||||
}
|
||||
|
||||
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');
|
||||
|
||||
// This fetches only active chats and clears archived chats, which will be fetched in `afterSync`
|
||||
|
Loading…
x
Reference in New Issue
Block a user