@mtcute/core 0.2.3 → 0.3.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 +33 -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 +179 -54
- package/cjs/network/session-connection.js.map +1 -1
- package/cjs/storage/abstract.d.ts +12 -1
- 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.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/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/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 +34 -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 +179 -54
- package/esm/network/session-connection.js.map +1 -1
- package/esm/storage/abstract.d.ts +12 -1
- 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.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/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/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 +4 -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 {
|
|
@@ -1047,14 +1130,10 @@ export class SessionConnection extends PersistentConnection {
|
|
|
1047
1130
|
// between multiple connections using the same session
|
|
1048
1131
|
this._flushTimer.emitWhenIdle();
|
|
1049
1132
|
}
|
|
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
1133
|
else {
|
|
1057
|
-
this.
|
|
1134
|
+
const nextPingTime = this._session.lastPingTime + PING_INTERVAL;
|
|
1135
|
+
const nextGetScheduleTime = this._session.getStateSchedule.raw[0]?.getState || Infinity;
|
|
1136
|
+
this._flushTimer.emitBefore(Math.min(nextPingTime, nextGetScheduleTime));
|
|
1058
1137
|
}
|
|
1059
1138
|
}
|
|
1060
1139
|
_doFlush() {
|
|
@@ -1069,6 +1148,8 @@ export class SessionConnection extends PersistentConnection {
|
|
|
1069
1148
|
let containerSize = 0;
|
|
1070
1149
|
let ackRequest = null;
|
|
1071
1150
|
let ackMsgIds = null;
|
|
1151
|
+
let getFutureSaltsRequest = null;
|
|
1152
|
+
let getFutureSaltsMsgId = null;
|
|
1072
1153
|
let pingRequest = null;
|
|
1073
1154
|
let pingId = null;
|
|
1074
1155
|
let pingMsgId = null;
|
|
@@ -1100,16 +1181,16 @@ export class SessionConnection extends PersistentConnection {
|
|
|
1100
1181
|
packetSize += ackRequest.length + 16;
|
|
1101
1182
|
messageCount += 1;
|
|
1102
1183
|
}
|
|
1103
|
-
|
|
1104
|
-
if (now - this._session.lastPingTime > 60000) {
|
|
1184
|
+
if (now - this._session.lastPingTime > PING_INTERVAL) {
|
|
1105
1185
|
if (!this._session.lastPingMsgId.isZero()) {
|
|
1106
1186
|
this.log.warn("didn't receive pong for previous ping (msg_id = %l)", this._session.lastPingMsgId);
|
|
1107
1187
|
this._session.pendingMessages.delete(this._session.lastPingMsgId);
|
|
1108
1188
|
}
|
|
1109
1189
|
pingId = randomLong();
|
|
1110
1190
|
const obj = {
|
|
1111
|
-
_: '
|
|
1191
|
+
_: 'mt_ping_delay_disconnect',
|
|
1112
1192
|
pingId,
|
|
1193
|
+
disconnectDelay: 75,
|
|
1113
1194
|
};
|
|
1114
1195
|
this._session.lastPingTime = Date.now();
|
|
1115
1196
|
pingRequest = TlBinaryWriter.serializeObject(this._writerMap, obj);
|
|
@@ -1174,6 +1255,16 @@ export class SessionConnection extends PersistentConnection {
|
|
|
1174
1255
|
destroySessions = this._queuedDestroySession;
|
|
1175
1256
|
this._queuedDestroySession = [];
|
|
1176
1257
|
}
|
|
1258
|
+
if (this._salts.shouldFetchSalts()) {
|
|
1259
|
+
const obj = {
|
|
1260
|
+
_: 'mt_get_future_salts',
|
|
1261
|
+
num: 64,
|
|
1262
|
+
};
|
|
1263
|
+
getFutureSaltsRequest = TlBinaryWriter.serializeObject(this._writerMap, obj);
|
|
1264
|
+
containerSize += getFutureSaltsRequest.length + 16;
|
|
1265
|
+
containerMessageCount += 1;
|
|
1266
|
+
this._salts.isFetching = true;
|
|
1267
|
+
}
|
|
1177
1268
|
let forceContainer = false;
|
|
1178
1269
|
const rpcToSend = [];
|
|
1179
1270
|
while (this._session.queuedRpc.length &&
|
|
@@ -1189,6 +1280,9 @@ export class SessionConnection extends PersistentConnection {
|
|
|
1189
1280
|
containerSize += msg.data.length + 16;
|
|
1190
1281
|
if (msg.gzipOverhead)
|
|
1191
1282
|
containerSize += msg.gzipOverhead;
|
|
1283
|
+
if (msg.chainId) {
|
|
1284
|
+
containerSize += INVOKE_AFTER_MSG_SIZE;
|
|
1285
|
+
}
|
|
1192
1286
|
// if message was already assigned a msg_id,
|
|
1193
1287
|
// we must wrap it in a container with a newer msg_id
|
|
1194
1288
|
if (msg.msgId)
|
|
@@ -1281,6 +1375,16 @@ export class SessionConnection extends PersistentConnection {
|
|
|
1281
1375
|
otherPendings.push(pending);
|
|
1282
1376
|
});
|
|
1283
1377
|
}
|
|
1378
|
+
if (getFutureSaltsRequest) {
|
|
1379
|
+
getFutureSaltsMsgId = this._registerOutgoingMsgId(this._session.writeMessage(writer, getFutureSaltsRequest));
|
|
1380
|
+
const pending = {
|
|
1381
|
+
_: 'future_salts',
|
|
1382
|
+
containerId: getFutureSaltsMsgId,
|
|
1383
|
+
};
|
|
1384
|
+
this._session.pendingMessages.set(getFutureSaltsMsgId, pending);
|
|
1385
|
+
otherPendings.push(pending);
|
|
1386
|
+
}
|
|
1387
|
+
const getStateTime = now + GET_STATE_INTERVAL;
|
|
1284
1388
|
for (let i = 0; i < rpcToSend.length; i++) {
|
|
1285
1389
|
const msg = rpcToSend[i];
|
|
1286
1390
|
// not using writeMessage here because we also need seqNo, and
|
|
@@ -1296,6 +1400,10 @@ export class SessionConnection extends PersistentConnection {
|
|
|
1296
1400
|
_: 'rpc',
|
|
1297
1401
|
rpc: msg,
|
|
1298
1402
|
});
|
|
1403
|
+
if (msg.chainId) {
|
|
1404
|
+
msg.invokeAfter = this._session.addToChain(msg.chainId, msgId);
|
|
1405
|
+
this.log.debug('chain %s: invoke %l after %l', msg.chainId, msg.msgId, msg.invokeAfter);
|
|
1406
|
+
}
|
|
1299
1407
|
}
|
|
1300
1408
|
else {
|
|
1301
1409
|
this.log.debug('%s: msg_id already assigned, reusing %l, seqno: %d', msg.method, msg.msgId, msg.seqNo);
|
|
@@ -1312,13 +1420,22 @@ export class SessionConnection extends PersistentConnection {
|
|
|
1312
1420
|
rootMsgId = msg.msgId;
|
|
1313
1421
|
writer.long(this._registerOutgoingMsgId(msg.msgId));
|
|
1314
1422
|
writer.uint(msg.seqNo);
|
|
1423
|
+
const invokeAfterSize = msg.invokeAfter ? INVOKE_AFTER_MSG_SIZE : 0;
|
|
1424
|
+
const writeInvokeAfter = () => {
|
|
1425
|
+
if (!msg.invokeAfter)
|
|
1426
|
+
return;
|
|
1427
|
+
writer.uint(INVOKE_AFTER_MSG_ID);
|
|
1428
|
+
writer.long(msg.invokeAfter);
|
|
1429
|
+
};
|
|
1315
1430
|
if (msg.gzipOverhead) {
|
|
1316
|
-
writer.uint(msg.data.length + msg.gzipOverhead);
|
|
1317
|
-
|
|
1431
|
+
writer.uint(msg.data.length + msg.gzipOverhead + invokeAfterSize);
|
|
1432
|
+
writeInvokeAfter();
|
|
1433
|
+
writer.uint(GZIP_PACKED_ID);
|
|
1318
1434
|
writer.bytes(msg.data);
|
|
1319
1435
|
}
|
|
1320
1436
|
else {
|
|
1321
|
-
writer.uint(msg.data.length);
|
|
1437
|
+
writer.uint(msg.data.length + invokeAfterSize);
|
|
1438
|
+
writeInvokeAfter();
|
|
1322
1439
|
writer.raw(msg.data);
|
|
1323
1440
|
}
|
|
1324
1441
|
msg.sent = true;
|
|
@@ -1327,7 +1444,7 @@ export class SessionConnection extends PersistentConnection {
|
|
|
1327
1444
|
// we now need to assign the container msg_id and seqno
|
|
1328
1445
|
// we couldn't have assigned them earlier because mtproto
|
|
1329
1446
|
// requires them to be >= than the contained messages
|
|
1330
|
-
|
|
1447
|
+
packetSize = writer.pos;
|
|
1331
1448
|
const containerId = this._session.getMessageId();
|
|
1332
1449
|
writer.pos = 0;
|
|
1333
1450
|
rootMsgId = containerId;
|
|
@@ -1353,9 +1470,9 @@ export class SessionConnection extends PersistentConnection {
|
|
|
1353
1470
|
});
|
|
1354
1471
|
}
|
|
1355
1472
|
const result = writer.result();
|
|
1356
|
-
this.log.debug('sending %d messages: size = %db, acks = %
|
|
1473
|
+
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
1474
|
const enc = this._session.encryptMessage(result);
|
|
1358
|
-
this.send(enc).catch((err) => {
|
|
1475
|
+
const promise = this.send(enc).catch((err) => {
|
|
1359
1476
|
this.log.error('error while sending pending messages (root msg_id = %l): %s', rootMsgId, err.stack);
|
|
1360
1477
|
// put acks in the front so they are the first to be sent
|
|
1361
1478
|
if (ackMsgIds) {
|
|
@@ -1365,6 +1482,14 @@ export class SessionConnection extends PersistentConnection {
|
|
|
1365
1482
|
this._onMessageFailed(rootMsgId, 'unknown error');
|
|
1366
1483
|
}
|
|
1367
1484
|
});
|
|
1485
|
+
if (this._inactivityPendingFlush && !this._session.hasPendingMessages) {
|
|
1486
|
+
void promise.then(() => {
|
|
1487
|
+
this.log.debug('pending messages sent, closing connection');
|
|
1488
|
+
this._flushTimer.reset();
|
|
1489
|
+
this._inactivityPendingFlush = false;
|
|
1490
|
+
super._onInactivityTimeout();
|
|
1491
|
+
});
|
|
1492
|
+
}
|
|
1368
1493
|
}
|
|
1369
1494
|
}
|
|
1370
1495
|
//# sourceMappingURL=session-connection.js.map
|