@mtcute/core 0.2.3 → 0.4.0
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/cjs/base-client.d.ts +33 -166
- package/cjs/base-client.js +69 -45
- package/cjs/base-client.js.map +1 -1
- package/cjs/base-client.types.d.ts +153 -0
- package/cjs/base-client.types.js +3 -0
- package/cjs/base-client.types.js.map +1 -0
- package/cjs/index.d.ts +1 -0
- package/cjs/index.js +1 -0
- package/cjs/index.js.map +1 -1
- package/cjs/network/mtproto-session.d.ts +10 -2
- package/cjs/network/mtproto-session.js +35 -3
- package/cjs/network/mtproto-session.js.map +1 -1
- package/cjs/network/multi-session-connection.d.ts +1 -1
- package/cjs/network/multi-session-connection.js +4 -3
- package/cjs/network/multi-session-connection.js.map +1 -1
- package/cjs/network/network-manager.d.ts +23 -0
- package/cjs/network/network-manager.js +28 -8
- package/cjs/network/network-manager.js.map +1 -1
- package/cjs/network/server-salt.d.ts +12 -0
- package/cjs/network/server-salt.js +45 -0
- package/cjs/network/server-salt.js.map +1 -0
- package/cjs/network/session-connection.d.ts +6 -1
- package/cjs/network/session-connection.js +193 -54
- package/cjs/network/session-connection.js.map +1 -1
- package/cjs/storage/abstract.d.ts +13 -2
- package/cjs/storage/abstract.js.map +1 -1
- package/cjs/storage/idb.d.ts +3 -1
- package/cjs/storage/idb.js +17 -0
- package/cjs/storage/idb.js.map +1 -1
- package/cjs/storage/index.d.ts +1 -0
- package/cjs/storage/index.js +1 -0
- package/cjs/storage/index.js.map +1 -1
- package/cjs/storage/json-file.d.ts +1 -2
- package/cjs/storage/json-file.js +4 -12
- package/cjs/storage/json-file.js.map +1 -1
- package/cjs/storage/json.js +1 -0
- package/cjs/storage/json.js.map +1 -1
- package/cjs/storage/memory.d.ts +5 -2
- package/cjs/storage/memory.js +16 -2
- package/cjs/storage/memory.js.map +1 -1
- package/cjs/storage/memory.test.js +2 -1
- package/cjs/storage/memory.test.js.map +1 -1
- package/cjs/utils/index.d.ts +1 -0
- package/cjs/utils/index.js +1 -0
- package/cjs/utils/index.js.map +1 -1
- package/cjs/utils/logger.js +8 -2
- package/cjs/utils/logger.js.map +1 -1
- package/cjs/utils/logger.test.js +12 -0
- package/cjs/utils/logger.test.js.map +1 -1
- package/cjs/utils/long-utils.d.ts +5 -0
- package/cjs/utils/long-utils.js +13 -1
- package/cjs/utils/long-utils.js.map +1 -1
- package/cjs/utils/platform/exit-hook.d.ts +1 -0
- package/cjs/utils/platform/exit-hook.js +45 -0
- package/cjs/utils/platform/exit-hook.js.map +1 -0
- package/cjs/utils/platform/exit-hook.web.d.ts +1 -0
- package/cjs/utils/platform/exit-hook.web.js +21 -0
- package/cjs/utils/platform/exit-hook.web.js.map +1 -0
- package/cjs/utils/sorted-array.js +1 -0
- package/cjs/utils/sorted-array.js.map +1 -1
- package/cjs/utils/type-assertions.d.ts +1 -0
- package/cjs/utils/type-assertions.js +7 -1
- package/cjs/utils/type-assertions.js.map +1 -1
- package/esm/base-client.d.ts +33 -166
- package/esm/base-client.js +69 -45
- package/esm/base-client.js.map +1 -1
- package/esm/base-client.types.d.ts +153 -0
- package/esm/base-client.types.js +2 -0
- package/esm/base-client.types.js.map +1 -0
- package/esm/index.d.ts +1 -0
- package/esm/index.js +1 -0
- package/esm/index.js.map +1 -1
- package/esm/network/mtproto-session.d.ts +10 -2
- package/esm/network/mtproto-session.js +36 -4
- package/esm/network/mtproto-session.js.map +1 -1
- package/esm/network/multi-session-connection.d.ts +1 -1
- package/esm/network/multi-session-connection.js +4 -3
- package/esm/network/multi-session-connection.js.map +1 -1
- package/esm/network/network-manager.d.ts +23 -0
- package/esm/network/network-manager.js +29 -9
- package/esm/network/network-manager.js.map +1 -1
- package/esm/network/server-salt.d.ts +12 -0
- package/esm/network/server-salt.js +38 -0
- package/esm/network/server-salt.js.map +1 -0
- package/esm/network/session-connection.d.ts +6 -1
- package/esm/network/session-connection.js +193 -54
- package/esm/network/session-connection.js.map +1 -1
- package/esm/storage/abstract.d.ts +13 -2
- package/esm/storage/abstract.js.map +1 -1
- package/esm/storage/idb.d.ts +3 -1
- package/esm/storage/idb.js +17 -0
- package/esm/storage/idb.js.map +1 -1
- package/esm/storage/index.d.ts +1 -0
- package/esm/storage/index.js +1 -0
- package/esm/storage/index.js.map +1 -1
- package/esm/storage/json-file.d.ts +1 -2
- package/esm/storage/json-file.js +4 -12
- package/esm/storage/json-file.js.map +1 -1
- package/esm/storage/json.js +1 -0
- package/esm/storage/json.js.map +1 -1
- package/esm/storage/memory.d.ts +5 -2
- package/esm/storage/memory.js +16 -2
- package/esm/storage/memory.js.map +1 -1
- package/esm/storage/memory.test.js +2 -1
- package/esm/storage/memory.test.js.map +1 -1
- package/esm/utils/index.d.ts +1 -0
- package/esm/utils/index.js +1 -0
- package/esm/utils/index.js.map +1 -1
- package/esm/utils/logger.js +8 -2
- package/esm/utils/logger.js.map +1 -1
- package/esm/utils/logger.test.js +12 -0
- package/esm/utils/logger.test.js.map +1 -1
- package/esm/utils/long-utils.d.ts +5 -0
- package/esm/utils/long-utils.js +11 -0
- package/esm/utils/long-utils.js.map +1 -1
- package/esm/utils/platform/exit-hook.d.ts +1 -0
- package/esm/utils/platform/exit-hook.js +41 -0
- package/esm/utils/platform/exit-hook.js.map +1 -0
- package/esm/utils/platform/exit-hook.web.d.ts +1 -0
- package/esm/utils/platform/exit-hook.web.js +17 -0
- package/esm/utils/platform/exit-hook.web.js.map +1 -0
- package/esm/utils/sorted-array.js +1 -0
- package/esm/utils/sorted-array.js.map +1 -1
- package/esm/utils/type-assertions.d.ts +1 -0
- package/esm/utils/type-assertions.js +5 -0
- package/esm/utils/type-assertions.js.map +1 -1
- package/package.json +6 -4
|
@@ -10,7 +10,9 @@ import { reportUnknownError } from '../utils/platform/error-reporting.js';
|
|
|
10
10
|
import { doAuthorization } from './authorization.js';
|
|
11
11
|
import { PersistentConnection } from './persistent-connection.js';
|
|
12
12
|
import { TransportError } from './transports/abstract.js';
|
|
13
|
-
const TEMP_AUTH_KEY_EXPIRY = 86400;
|
|
13
|
+
const TEMP_AUTH_KEY_EXPIRY = 86400; // 24 hours
|
|
14
|
+
const PING_INTERVAL = 60000; // 1 minute
|
|
15
|
+
const GET_STATE_INTERVAL = 1500; // 1.5 seconds
|
|
14
16
|
// destroy_auth_key#d1435160 = DestroyAuthKeyRes;
|
|
15
17
|
// const DESTROY_AUTH_KEY = Buffer.from('605134d1', 'hex')
|
|
16
18
|
// gzip_packed#3072cfa1 packed_data:string = Object;
|
|
@@ -19,6 +21,9 @@ const GZIP_PACKED_ID = 0x3072cfa1;
|
|
|
19
21
|
const MSG_CONTAINER_ID = 0x73f1f8dc;
|
|
20
22
|
// rpc_result#f35c6d01 req_msg_id:long result:Object = RpcResult;
|
|
21
23
|
const RPC_RESULT_ID = 0xf35c6d01;
|
|
24
|
+
// invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X;
|
|
25
|
+
const INVOKE_AFTER_MSG_ID = 0xcb9f372d;
|
|
26
|
+
const INVOKE_AFTER_MSG_SIZE = 12; // 8 (invokeAfterMsg) + 4 (msg_id)
|
|
22
27
|
function makeNiceStack(error, stack, method) {
|
|
23
28
|
error.stack = `RpcError (${error.code} ${error.text}): ${error.message}\n at ${method}\n${stack
|
|
24
29
|
.split('\n')
|
|
@@ -44,6 +49,7 @@ export class SessionConnection extends PersistentConnection {
|
|
|
44
49
|
this._readerMap = params.readerMap;
|
|
45
50
|
this._writerMap = params.writerMap;
|
|
46
51
|
this._crypto = params.crypto;
|
|
52
|
+
this._salts = params.salts;
|
|
47
53
|
this._handleRawMessage = this._handleRawMessage.bind(this);
|
|
48
54
|
}
|
|
49
55
|
getAuthKey(temp = false) {
|
|
@@ -81,6 +87,7 @@ export class SessionConnection extends PersistentConnection {
|
|
|
81
87
|
reset(forever = false) {
|
|
82
88
|
this._session.initConnectionCalled = false;
|
|
83
89
|
this._flushTimer.reset();
|
|
90
|
+
this._salts.isFetching = false;
|
|
84
91
|
if (forever) {
|
|
85
92
|
this.removeAllListeners();
|
|
86
93
|
}
|
|
@@ -182,7 +189,7 @@ export class SessionConnection extends PersistentConnection {
|
|
|
182
189
|
doAuthorization(this, this._crypto)
|
|
183
190
|
.then(([authKey, serverSalt, timeOffset]) => {
|
|
184
191
|
this._session._authKey.setup(authKey);
|
|
185
|
-
this.
|
|
192
|
+
this._salts.currentSalt = serverSalt;
|
|
186
193
|
this._session._timeOffset = timeOffset;
|
|
187
194
|
this._session.authorizationPending = false;
|
|
188
195
|
this.emit('key-change', authKey);
|
|
@@ -300,7 +307,7 @@ export class SessionConnection extends PersistentConnection {
|
|
|
300
307
|
// and primary is not immediately forgot because messages using it may still be in flight)
|
|
301
308
|
this._session._authKeyTempSecondary = this._session._authKeyTemp;
|
|
302
309
|
this._session._authKeyTemp = tempKey;
|
|
303
|
-
this.
|
|
310
|
+
this._salts.currentSalt = tempServerSalt;
|
|
304
311
|
this.log.debug('temp key has been bound, exp = %d', inner.expiresAt);
|
|
305
312
|
this._isPfsBindingPending = false;
|
|
306
313
|
this._isPfsBindingPendingInBackground = false;
|
|
@@ -439,7 +446,7 @@ export class SessionConnection extends PersistentConnection {
|
|
|
439
446
|
this._onMsgsStateInfo(message);
|
|
440
447
|
break;
|
|
441
448
|
case 'mt_future_salts':
|
|
442
|
-
|
|
449
|
+
this._onFutureSalts(message);
|
|
443
450
|
break;
|
|
444
451
|
case 'mt_msgs_state_req':
|
|
445
452
|
case 'mt_msg_resend_req':
|
|
@@ -520,7 +527,7 @@ export class SessionConnection extends PersistentConnection {
|
|
|
520
527
|
this.log.debug('failed to parse rpc_result for cancel request %l, ignoring', reqMsgId);
|
|
521
528
|
return;
|
|
522
529
|
}
|
|
523
|
-
this.log.debug('received %s for cancelled request %l: %j', result._, reqMsgId);
|
|
530
|
+
this.log.debug('received %s for cancelled request %l: %j', result._, reqMsgId, result);
|
|
524
531
|
this._onMessageAcked(reqMsgId);
|
|
525
532
|
return;
|
|
526
533
|
}
|
|
@@ -546,6 +553,7 @@ export class SessionConnection extends PersistentConnection {
|
|
|
546
553
|
}
|
|
547
554
|
// initConnection call was definitely received and
|
|
548
555
|
// processed by the server, so we no longer need to use it
|
|
556
|
+
// todo: is this the case with failed invokeAfterMsg(s) as well?
|
|
549
557
|
if (rpc.initConn) {
|
|
550
558
|
this._session.initConnectionCalled = true;
|
|
551
559
|
}
|
|
@@ -554,37 +562,61 @@ export class SessionConnection extends PersistentConnection {
|
|
|
554
562
|
if (result._ === 'mt_rpc_error') {
|
|
555
563
|
const res = result;
|
|
556
564
|
this.log.debug('received rpc_error [%d:%s] for %l (%s)', res.errorCode, res.errorMessage, reqMsgId, rpc.method);
|
|
557
|
-
if (res.errorMessage === 'AUTH_KEY_PERM_EMPTY') {
|
|
558
|
-
// happens when temp auth key is not yet bound
|
|
559
|
-
// this shouldn't happen as we block any outbound communications
|
|
560
|
-
// until the temp key is derived and bound.
|
|
561
|
-
//
|
|
562
|
-
// i think it is also possible for the error to be returned
|
|
563
|
-
// when the temp key has expired, but this still shouldn't happen
|
|
564
|
-
// but this is tg, so something may go wrong, and we will receive this as an error
|
|
565
|
-
// (for god's sake why is this not in mtproto and instead hacked into the app layer)
|
|
566
|
-
this._authorizePfs();
|
|
567
|
-
this._onMessageFailed(reqMsgId, 'AUTH_KEY_PERM_EMPTY', true);
|
|
568
|
-
return;
|
|
569
|
-
}
|
|
570
|
-
if (res.errorMessage === 'CONNECTION_NOT_INITED') {
|
|
571
|
-
// this seems to sometimes happen when using pfs
|
|
572
|
-
// no idea why, but tdlib also seems to handle these, so whatever
|
|
573
|
-
this._session.initConnectionCalled = false;
|
|
574
|
-
this._onMessageFailed(reqMsgId, res.errorMessage, true);
|
|
575
|
-
// just setting this flag is not enough because the message
|
|
576
|
-
// is already serialized, so we do this awesome hack
|
|
577
|
-
this.sendRpc({ _: 'help.getNearestDc' })
|
|
578
|
-
.then(() => {
|
|
579
|
-
this.log.debug('additional help.getNearestDc for initConnection ok');
|
|
580
|
-
})
|
|
581
|
-
.catch((err) => {
|
|
582
|
-
this.log.debug('additional help.getNearestDc for initConnection error: %s', err);
|
|
583
|
-
});
|
|
584
|
-
return;
|
|
585
|
-
}
|
|
586
565
|
if (rpc.cancelled)
|
|
587
566
|
return;
|
|
567
|
+
switch (res.errorMessage) {
|
|
568
|
+
case 'AUTH_KEY_PERM_EMPTY':
|
|
569
|
+
// happens when temp auth key is not yet bound
|
|
570
|
+
// this shouldn't happen as we block any outbound communications
|
|
571
|
+
// until the temp key is derived and bound.
|
|
572
|
+
//
|
|
573
|
+
// i think it is also possible for the error to be returned
|
|
574
|
+
// when the temp key has expired, but this still shouldn't happen
|
|
575
|
+
// but this is tg, so something may go wrong, and we will receive this as an error
|
|
576
|
+
// (for god's sake why is this not in mtproto and instead hacked into the app layer)
|
|
577
|
+
this._authorizePfs();
|
|
578
|
+
this._onMessageFailed(reqMsgId, 'AUTH_KEY_PERM_EMPTY', true);
|
|
579
|
+
return;
|
|
580
|
+
case 'CONNECTION_NOT_INITED': {
|
|
581
|
+
// this seems to sometimes happen when using pfs
|
|
582
|
+
// no idea why, but tdlib also seems to handle these, so whatever
|
|
583
|
+
this._session.initConnectionCalled = false;
|
|
584
|
+
this._onMessageFailed(reqMsgId, res.errorMessage, true);
|
|
585
|
+
// just setting this flag is not enough because the message
|
|
586
|
+
// is already serialized, so we do this awesome hack
|
|
587
|
+
this.sendRpc({ _: 'help.getNearestDc' })
|
|
588
|
+
.then(() => {
|
|
589
|
+
this.log.debug('additional help.getNearestDc for initConnection ok');
|
|
590
|
+
})
|
|
591
|
+
.catch((err) => {
|
|
592
|
+
this.log.debug('additional help.getNearestDc for initConnection error: %s', err);
|
|
593
|
+
});
|
|
594
|
+
return;
|
|
595
|
+
}
|
|
596
|
+
case 'MSG_WAIT_TIMEOUT':
|
|
597
|
+
case 'MSG_WAIT_FAILED': {
|
|
598
|
+
if (!rpc.invokeAfter) {
|
|
599
|
+
this.log.warn('received %s for non-chained request %l', res.errorMessage, reqMsgId);
|
|
600
|
+
break;
|
|
601
|
+
}
|
|
602
|
+
// in some cases, MSG_WAIT_TIMEOUT is returned instead of MSG_WAIT_FAILED when one of the deps
|
|
603
|
+
// failed with MSG_WAIT_TIMEOUT. i have no clue why, this makes zero sense, but what fucking ever
|
|
604
|
+
//
|
|
605
|
+
// this basically means we can't handle a timeout any different than a general failure,
|
|
606
|
+
// because the timeout might not refer to the immediate `.invokeAfter` message, but to
|
|
607
|
+
// its arbitrary-depth dependency, so we indeed have to wait for the message ourselves...
|
|
608
|
+
if (this._session.pendingMessages.has(rpc.invokeAfter)) {
|
|
609
|
+
// the dependency is still pending, postpone the processing
|
|
610
|
+
this.log.debug('chain %s: waiting for %l before processing %l', rpc.chainId, rpc.invokeAfter, reqMsgId);
|
|
611
|
+
this._session.getPendingChainedFails(rpc.chainId).insert(rpc);
|
|
612
|
+
}
|
|
613
|
+
else {
|
|
614
|
+
this._session.chains.delete(rpc.chainId);
|
|
615
|
+
this._onMessageFailed(reqMsgId, 'MSG_WAIT_FAILED', true);
|
|
616
|
+
}
|
|
617
|
+
return;
|
|
618
|
+
}
|
|
619
|
+
}
|
|
588
620
|
const error = tl.RpcError.fromTl(res);
|
|
589
621
|
if (this.params.niceStacks !== false) {
|
|
590
622
|
makeNiceStack(error, rpc.stack, rpc.method);
|
|
@@ -600,9 +632,31 @@ export class SessionConnection extends PersistentConnection {
|
|
|
600
632
|
return;
|
|
601
633
|
rpc.promise.resolve(result);
|
|
602
634
|
}
|
|
635
|
+
if (rpc.chainId) {
|
|
636
|
+
this._processPendingChainedFails(rpc.chainId, reqMsgId);
|
|
637
|
+
}
|
|
603
638
|
this._onMessageAcked(reqMsgId);
|
|
604
639
|
this._session.pendingMessages.delete(reqMsgId);
|
|
605
640
|
}
|
|
641
|
+
_processPendingChainedFails(chainId, sinceMsgId) {
|
|
642
|
+
// sinceMsgId was already definitely received and contained an error.
|
|
643
|
+
// we should now re-send all the pending MSG_WAIT_FAILED after it
|
|
644
|
+
this._session.removeFromChain(chainId, sinceMsgId);
|
|
645
|
+
const oldPending = this._session.chainsPendingFails.get(chainId);
|
|
646
|
+
if (!oldPending?.length) {
|
|
647
|
+
return;
|
|
648
|
+
}
|
|
649
|
+
const idx = oldPending.index({ invokeAfter: sinceMsgId }, true);
|
|
650
|
+
if (idx === -1)
|
|
651
|
+
return;
|
|
652
|
+
const toFail = oldPending.raw.splice(idx);
|
|
653
|
+
this.log.debug('chain %s: failing %d dependant messages: %L', chainId, toFail.length, toFail);
|
|
654
|
+
// we're failing the rest of the chain, including the last message
|
|
655
|
+
this._session.chains.delete(chainId);
|
|
656
|
+
for (const rpc of toFail) {
|
|
657
|
+
this._onMessageFailed(rpc.msgId, 'MSG_WAIT_FAILED', true);
|
|
658
|
+
}
|
|
659
|
+
}
|
|
606
660
|
_onMessageAcked(msgId, inContainer = false) {
|
|
607
661
|
const msg = this._session.pendingMessages.get(msgId);
|
|
608
662
|
if (!msg) {
|
|
@@ -634,6 +688,7 @@ export class SessionConnection extends PersistentConnection {
|
|
|
634
688
|
break;
|
|
635
689
|
}
|
|
636
690
|
case 'bind':
|
|
691
|
+
case 'cancel':
|
|
637
692
|
break; // do nothing, wait for the result
|
|
638
693
|
default:
|
|
639
694
|
if (!inContainer) {
|
|
@@ -708,6 +763,12 @@ export class SessionConnection extends PersistentConnection {
|
|
|
708
763
|
case 'bind':
|
|
709
764
|
this.log.debug('temp key binding request %l failed because of %s, retrying', msgId, reason);
|
|
710
765
|
msgInfo.promise.reject(Error(reason));
|
|
766
|
+
break;
|
|
767
|
+
case 'future_salts':
|
|
768
|
+
this.log.debug('future_salts request %l failed because of %s, will retry', msgId, reason);
|
|
769
|
+
this._salts.isFetching = false;
|
|
770
|
+
// this is enough to make it retry on the next flush.
|
|
771
|
+
break;
|
|
711
772
|
}
|
|
712
773
|
this._session.pendingMessages.delete(msgId);
|
|
713
774
|
}
|
|
@@ -737,7 +798,7 @@ export class SessionConnection extends PersistentConnection {
|
|
|
737
798
|
this._session.resetLastPing();
|
|
738
799
|
}
|
|
739
800
|
_onBadServerSalt(msg) {
|
|
740
|
-
this.
|
|
801
|
+
this._salts.currentSalt = msg.newServerSalt;
|
|
741
802
|
this._onMessageFailed(msg.badMsgId, 'bad_server_salt');
|
|
742
803
|
}
|
|
743
804
|
_onBadMsgNotification(msgId, msg) {
|
|
@@ -775,7 +836,7 @@ export class SessionConnection extends PersistentConnection {
|
|
|
775
836
|
// just been established, and the client will fetch them anyways
|
|
776
837
|
this.emit('update', { _: 'updatesTooLong' });
|
|
777
838
|
}
|
|
778
|
-
this.
|
|
839
|
+
this._salts.currentSalt = serverSalt;
|
|
779
840
|
this.log.debug('received new_session_created, uid = %l, first msg_id = %l', uniqueId, firstMsgId);
|
|
780
841
|
for (const msgId of this._session.pendingMessages.keys()) {
|
|
781
842
|
const val = this._session.pendingMessages.get(msgId);
|
|
@@ -814,7 +875,7 @@ export class SessionConnection extends PersistentConnection {
|
|
|
814
875
|
case 2:
|
|
815
876
|
case 3:
|
|
816
877
|
// message wasn't received by the server
|
|
817
|
-
this._onMessageFailed(msgId, `message info state ${status}`);
|
|
878
|
+
return this._onMessageFailed(msgId, `message info state ${status}`);
|
|
818
879
|
break;
|
|
819
880
|
case 0:
|
|
820
881
|
if (!answerMsgId.isZero()) {
|
|
@@ -833,6 +894,10 @@ export class SessionConnection extends PersistentConnection {
|
|
|
833
894
|
this._flushTimer.emitWhenIdle();
|
|
834
895
|
return;
|
|
835
896
|
}
|
|
897
|
+
if (answerMsgId.isZero()) {
|
|
898
|
+
this.log.debug('received message info for %l: message is still pending (status = %d)', msgId, status);
|
|
899
|
+
return;
|
|
900
|
+
}
|
|
836
901
|
this.log.debug('received message info for %l, and answer (%l) was already received', msgId, answerMsgId);
|
|
837
902
|
}
|
|
838
903
|
_onMessagesInfo(msgIds, info) {
|
|
@@ -855,6 +920,21 @@ export class SessionConnection extends PersistentConnection {
|
|
|
855
920
|
}
|
|
856
921
|
this._onMessagesInfo(info.msgIds, msg.info);
|
|
857
922
|
}
|
|
923
|
+
_onFutureSalts(msg) {
|
|
924
|
+
const info = this._session.pendingMessages.get(msg.reqMsgId);
|
|
925
|
+
if (!info) {
|
|
926
|
+
this.log.warn('received future_salts to unknown request %l', msg.reqMsgId);
|
|
927
|
+
return;
|
|
928
|
+
}
|
|
929
|
+
if (info._ !== 'future_salts') {
|
|
930
|
+
this.log.warn('received future_salts to %s query %l', info._, msg.reqMsgId);
|
|
931
|
+
return;
|
|
932
|
+
}
|
|
933
|
+
this.log.debug('received future_salts: %d salts', msg.salts.length);
|
|
934
|
+
this._salts.isFetching = false;
|
|
935
|
+
this._salts.setFutureSalts(msg.salts.slice());
|
|
936
|
+
this.emit('future-salts', msg.salts);
|
|
937
|
+
}
|
|
858
938
|
_onDestroySessionResult(msg) {
|
|
859
939
|
const reqMsgId = this._session.destroySessionIdToMsgId.get(msg.sessionId);
|
|
860
940
|
if (!reqMsgId) {
|
|
@@ -886,7 +966,7 @@ export class SessionConnection extends PersistentConnection {
|
|
|
886
966
|
this._flushTimer.emitWhenIdle();
|
|
887
967
|
}
|
|
888
968
|
}
|
|
889
|
-
sendRpc(request, stack, timeout, abortSignal) {
|
|
969
|
+
sendRpc(request, stack, timeout, abortSignal, chainId) {
|
|
890
970
|
if (this._usable && this.params.inactivityTimeout) {
|
|
891
971
|
this._rescheduleInactivity();
|
|
892
972
|
}
|
|
@@ -957,7 +1037,9 @@ export class SessionConnection extends PersistentConnection {
|
|
|
957
1037
|
// we will need to know size of gzip_packed overhead in _flush()
|
|
958
1038
|
gzipOverhead: shouldGzip ? 4 + TlSerializationCounter.countBytesOverhead(content.length) : 0,
|
|
959
1039
|
initConn,
|
|
1040
|
+
chainId,
|
|
960
1041
|
// setting them as well so jit can optimize stuff
|
|
1042
|
+
invokeAfter: undefined,
|
|
961
1043
|
sent: undefined,
|
|
962
1044
|
done: undefined,
|
|
963
1045
|
getState: undefined,
|
|
@@ -1004,6 +1086,7 @@ export class SessionConnection extends PersistentConnection {
|
|
|
1004
1086
|
rpc.cancelled = true;
|
|
1005
1087
|
if (rpc.msgId) {
|
|
1006
1088
|
this._session.queuedCancelReq.push(rpc.msgId);
|
|
1089
|
+
this._session.getStateSchedule.remove(rpc);
|
|
1007
1090
|
this._flushTimer.emitWhenIdle();
|
|
1008
1091
|
}
|
|
1009
1092
|
else {
|
|
@@ -1015,6 +1098,20 @@ export class SessionConnection extends PersistentConnection {
|
|
|
1015
1098
|
_onInactivityTimeout() {
|
|
1016
1099
|
// we should send all pending acks and other service messages
|
|
1017
1100
|
// before dropping the connection
|
|
1101
|
+
// additionally, if we are still waiting for some rpc results,
|
|
1102
|
+
// we should wait for them first
|
|
1103
|
+
let hasPendingRpc = false;
|
|
1104
|
+
for (const it of this._session.pendingMessages.values()) {
|
|
1105
|
+
if (it._ === 'rpc') {
|
|
1106
|
+
hasPendingRpc = true;
|
|
1107
|
+
break;
|
|
1108
|
+
}
|
|
1109
|
+
}
|
|
1110
|
+
if (hasPendingRpc) {
|
|
1111
|
+
this.log.debug('waiting for pending rpc queries to finish before closing connection');
|
|
1112
|
+
this._rescheduleInactivity();
|
|
1113
|
+
return;
|
|
1114
|
+
}
|
|
1018
1115
|
if (!this._session.hasPendingMessages) {
|
|
1019
1116
|
this.log.debug('no pending service messages, closing connection');
|
|
1020
1117
|
super._onInactivityTimeout();
|
|
@@ -1047,14 +1144,10 @@ export class SessionConnection extends PersistentConnection {
|
|
|
1047
1144
|
// between multiple connections using the same session
|
|
1048
1145
|
this._flushTimer.emitWhenIdle();
|
|
1049
1146
|
}
|
|
1050
|
-
else if (this._inactivityPendingFlush) {
|
|
1051
|
-
this.log.debug('pending messages sent, closing connection');
|
|
1052
|
-
this._flushTimer.reset();
|
|
1053
|
-
this._inactivityPendingFlush = false;
|
|
1054
|
-
super._onInactivityTimeout();
|
|
1055
|
-
}
|
|
1056
1147
|
else {
|
|
1057
|
-
this.
|
|
1148
|
+
const nextPingTime = this._session.lastPingTime + PING_INTERVAL;
|
|
1149
|
+
const nextGetScheduleTime = this._session.getStateSchedule.raw[0]?.getState || Infinity;
|
|
1150
|
+
this._flushTimer.emitBefore(Math.min(nextPingTime, nextGetScheduleTime));
|
|
1058
1151
|
}
|
|
1059
1152
|
}
|
|
1060
1153
|
_doFlush() {
|
|
@@ -1069,6 +1162,8 @@ export class SessionConnection extends PersistentConnection {
|
|
|
1069
1162
|
let containerSize = 0;
|
|
1070
1163
|
let ackRequest = null;
|
|
1071
1164
|
let ackMsgIds = null;
|
|
1165
|
+
let getFutureSaltsRequest = null;
|
|
1166
|
+
let getFutureSaltsMsgId = null;
|
|
1072
1167
|
let pingRequest = null;
|
|
1073
1168
|
let pingId = null;
|
|
1074
1169
|
let pingMsgId = null;
|
|
@@ -1100,16 +1195,16 @@ export class SessionConnection extends PersistentConnection {
|
|
|
1100
1195
|
packetSize += ackRequest.length + 16;
|
|
1101
1196
|
messageCount += 1;
|
|
1102
1197
|
}
|
|
1103
|
-
|
|
1104
|
-
if (now - this._session.lastPingTime > 60000) {
|
|
1198
|
+
if (now - this._session.lastPingTime > PING_INTERVAL) {
|
|
1105
1199
|
if (!this._session.lastPingMsgId.isZero()) {
|
|
1106
1200
|
this.log.warn("didn't receive pong for previous ping (msg_id = %l)", this._session.lastPingMsgId);
|
|
1107
1201
|
this._session.pendingMessages.delete(this._session.lastPingMsgId);
|
|
1108
1202
|
}
|
|
1109
1203
|
pingId = randomLong();
|
|
1110
1204
|
const obj = {
|
|
1111
|
-
_: '
|
|
1205
|
+
_: 'mt_ping_delay_disconnect',
|
|
1112
1206
|
pingId,
|
|
1207
|
+
disconnectDelay: 75,
|
|
1113
1208
|
};
|
|
1114
1209
|
this._session.lastPingTime = Date.now();
|
|
1115
1210
|
pingRequest = TlBinaryWriter.serializeObject(this._writerMap, obj);
|
|
@@ -1174,6 +1269,16 @@ export class SessionConnection extends PersistentConnection {
|
|
|
1174
1269
|
destroySessions = this._queuedDestroySession;
|
|
1175
1270
|
this._queuedDestroySession = [];
|
|
1176
1271
|
}
|
|
1272
|
+
if (this._salts.shouldFetchSalts()) {
|
|
1273
|
+
const obj = {
|
|
1274
|
+
_: 'mt_get_future_salts',
|
|
1275
|
+
num: 64,
|
|
1276
|
+
};
|
|
1277
|
+
getFutureSaltsRequest = TlBinaryWriter.serializeObject(this._writerMap, obj);
|
|
1278
|
+
containerSize += getFutureSaltsRequest.length + 16;
|
|
1279
|
+
containerMessageCount += 1;
|
|
1280
|
+
this._salts.isFetching = true;
|
|
1281
|
+
}
|
|
1177
1282
|
let forceContainer = false;
|
|
1178
1283
|
const rpcToSend = [];
|
|
1179
1284
|
while (this._session.queuedRpc.length &&
|
|
@@ -1189,6 +1294,9 @@ export class SessionConnection extends PersistentConnection {
|
|
|
1189
1294
|
containerSize += msg.data.length + 16;
|
|
1190
1295
|
if (msg.gzipOverhead)
|
|
1191
1296
|
containerSize += msg.gzipOverhead;
|
|
1297
|
+
if (msg.chainId) {
|
|
1298
|
+
containerSize += INVOKE_AFTER_MSG_SIZE;
|
|
1299
|
+
}
|
|
1192
1300
|
// if message was already assigned a msg_id,
|
|
1193
1301
|
// we must wrap it in a container with a newer msg_id
|
|
1194
1302
|
if (msg.msgId)
|
|
@@ -1281,6 +1389,16 @@ export class SessionConnection extends PersistentConnection {
|
|
|
1281
1389
|
otherPendings.push(pending);
|
|
1282
1390
|
});
|
|
1283
1391
|
}
|
|
1392
|
+
if (getFutureSaltsRequest) {
|
|
1393
|
+
getFutureSaltsMsgId = this._registerOutgoingMsgId(this._session.writeMessage(writer, getFutureSaltsRequest));
|
|
1394
|
+
const pending = {
|
|
1395
|
+
_: 'future_salts',
|
|
1396
|
+
containerId: getFutureSaltsMsgId,
|
|
1397
|
+
};
|
|
1398
|
+
this._session.pendingMessages.set(getFutureSaltsMsgId, pending);
|
|
1399
|
+
otherPendings.push(pending);
|
|
1400
|
+
}
|
|
1401
|
+
const getStateTime = now + GET_STATE_INTERVAL;
|
|
1284
1402
|
for (let i = 0; i < rpcToSend.length; i++) {
|
|
1285
1403
|
const msg = rpcToSend[i];
|
|
1286
1404
|
// not using writeMessage here because we also need seqNo, and
|
|
@@ -1296,6 +1414,10 @@ export class SessionConnection extends PersistentConnection {
|
|
|
1296
1414
|
_: 'rpc',
|
|
1297
1415
|
rpc: msg,
|
|
1298
1416
|
});
|
|
1417
|
+
if (msg.chainId) {
|
|
1418
|
+
msg.invokeAfter = this._session.addToChain(msg.chainId, msgId);
|
|
1419
|
+
this.log.debug('chain %s: invoke %l after %l', msg.chainId, msg.msgId, msg.invokeAfter);
|
|
1420
|
+
}
|
|
1299
1421
|
}
|
|
1300
1422
|
else {
|
|
1301
1423
|
this.log.debug('%s: msg_id already assigned, reusing %l, seqno: %d', msg.method, msg.msgId, msg.seqNo);
|
|
@@ -1312,13 +1434,22 @@ export class SessionConnection extends PersistentConnection {
|
|
|
1312
1434
|
rootMsgId = msg.msgId;
|
|
1313
1435
|
writer.long(this._registerOutgoingMsgId(msg.msgId));
|
|
1314
1436
|
writer.uint(msg.seqNo);
|
|
1437
|
+
const invokeAfterSize = msg.invokeAfter ? INVOKE_AFTER_MSG_SIZE : 0;
|
|
1438
|
+
const writeInvokeAfter = () => {
|
|
1439
|
+
if (!msg.invokeAfter)
|
|
1440
|
+
return;
|
|
1441
|
+
writer.uint(INVOKE_AFTER_MSG_ID);
|
|
1442
|
+
writer.long(msg.invokeAfter);
|
|
1443
|
+
};
|
|
1315
1444
|
if (msg.gzipOverhead) {
|
|
1316
|
-
writer.uint(msg.data.length + msg.gzipOverhead);
|
|
1317
|
-
|
|
1445
|
+
writer.uint(msg.data.length + msg.gzipOverhead + invokeAfterSize);
|
|
1446
|
+
writeInvokeAfter();
|
|
1447
|
+
writer.uint(GZIP_PACKED_ID);
|
|
1318
1448
|
writer.bytes(msg.data);
|
|
1319
1449
|
}
|
|
1320
1450
|
else {
|
|
1321
|
-
writer.uint(msg.data.length);
|
|
1451
|
+
writer.uint(msg.data.length + invokeAfterSize);
|
|
1452
|
+
writeInvokeAfter();
|
|
1322
1453
|
writer.raw(msg.data);
|
|
1323
1454
|
}
|
|
1324
1455
|
msg.sent = true;
|
|
@@ -1327,7 +1458,7 @@ export class SessionConnection extends PersistentConnection {
|
|
|
1327
1458
|
// we now need to assign the container msg_id and seqno
|
|
1328
1459
|
// we couldn't have assigned them earlier because mtproto
|
|
1329
1460
|
// requires them to be >= than the contained messages
|
|
1330
|
-
|
|
1461
|
+
packetSize = writer.pos;
|
|
1331
1462
|
const containerId = this._session.getMessageId();
|
|
1332
1463
|
writer.pos = 0;
|
|
1333
1464
|
rootMsgId = containerId;
|
|
@@ -1353,9 +1484,9 @@ export class SessionConnection extends PersistentConnection {
|
|
|
1353
1484
|
});
|
|
1354
1485
|
}
|
|
1355
1486
|
const result = writer.result();
|
|
1356
|
-
this.log.debug('sending %d messages: size = %db, acks = %
|
|
1487
|
+
this.log.debug('sending %d messages: size = %db, acks = %L, ping = %b (msg_id = %l), state_req = %L (msg_id = %l), resend = %L (msg_id = %l), cancels = %L (msg_id = %l), salts_req = %b (msg_id = %l), rpc = %s, container = %b, root msg_id = %l', messageCount, packetSize, ackMsgIds, pingRequest, pingMsgId, getStateMsgIds, getStateMsgId, resendMsgIds, cancelRpcs, cancelRpcs, resendMsgId, getFutureSaltsRequest, getFutureSaltsMsgId, rpcToSend.map((it) => it.method), useContainer, rootMsgId);
|
|
1357
1488
|
const enc = this._session.encryptMessage(result);
|
|
1358
|
-
this.send(enc).catch((err) => {
|
|
1489
|
+
const promise = this.send(enc).catch((err) => {
|
|
1359
1490
|
this.log.error('error while sending pending messages (root msg_id = %l): %s', rootMsgId, err.stack);
|
|
1360
1491
|
// put acks in the front so they are the first to be sent
|
|
1361
1492
|
if (ackMsgIds) {
|
|
@@ -1365,6 +1496,14 @@ export class SessionConnection extends PersistentConnection {
|
|
|
1365
1496
|
this._onMessageFailed(rootMsgId, 'unknown error');
|
|
1366
1497
|
}
|
|
1367
1498
|
});
|
|
1499
|
+
if (this._inactivityPendingFlush && !this._session.hasPendingMessages) {
|
|
1500
|
+
void promise.then(() => {
|
|
1501
|
+
this.log.debug('pending messages sent, closing connection');
|
|
1502
|
+
this._flushTimer.reset();
|
|
1503
|
+
this._inactivityPendingFlush = false;
|
|
1504
|
+
super._onInactivityTimeout();
|
|
1505
|
+
});
|
|
1506
|
+
}
|
|
1368
1507
|
}
|
|
1369
1508
|
}
|
|
1370
1509
|
//# sourceMappingURL=session-connection.js.map
|