@mtcute/core 0.2.2 → 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.
Files changed (103) hide show
  1. package/cjs/base-client.d.ts +33 -166
  2. package/cjs/base-client.js +70 -48
  3. package/cjs/base-client.js.map +1 -1
  4. package/cjs/base-client.types.d.ts +153 -0
  5. package/cjs/base-client.types.js +3 -0
  6. package/cjs/base-client.types.js.map +1 -0
  7. package/cjs/index.d.ts +1 -0
  8. package/cjs/index.js +1 -0
  9. package/cjs/index.js.map +1 -1
  10. package/cjs/network/mtproto-session.d.ts +10 -2
  11. package/cjs/network/mtproto-session.js +33 -3
  12. package/cjs/network/mtproto-session.js.map +1 -1
  13. package/cjs/network/multi-session-connection.d.ts +1 -1
  14. package/cjs/network/multi-session-connection.js +4 -3
  15. package/cjs/network/multi-session-connection.js.map +1 -1
  16. package/cjs/network/network-manager.d.ts +23 -0
  17. package/cjs/network/network-manager.js +28 -8
  18. package/cjs/network/network-manager.js.map +1 -1
  19. package/cjs/network/server-salt.d.ts +12 -0
  20. package/cjs/network/server-salt.js +45 -0
  21. package/cjs/network/server-salt.js.map +1 -0
  22. package/cjs/network/session-connection.d.ts +6 -1
  23. package/cjs/network/session-connection.js +179 -54
  24. package/cjs/network/session-connection.js.map +1 -1
  25. package/cjs/storage/abstract.d.ts +12 -1
  26. package/cjs/storage/abstract.js.map +1 -1
  27. package/cjs/storage/idb.d.ts +3 -1
  28. package/cjs/storage/idb.js +17 -0
  29. package/cjs/storage/idb.js.map +1 -1
  30. package/cjs/storage/index.d.ts +1 -0
  31. package/cjs/storage/index.js +1 -0
  32. package/cjs/storage/index.js.map +1 -1
  33. package/cjs/storage/json.js +1 -0
  34. package/cjs/storage/json.js.map +1 -1
  35. package/cjs/storage/memory.d.ts +5 -2
  36. package/cjs/storage/memory.js +16 -2
  37. package/cjs/storage/memory.js.map +1 -1
  38. package/cjs/storage/memory.test.js +2 -1
  39. package/cjs/storage/memory.test.js.map +1 -1
  40. package/cjs/utils/logger.js +8 -2
  41. package/cjs/utils/logger.js.map +1 -1
  42. package/cjs/utils/logger.test.js +12 -0
  43. package/cjs/utils/logger.test.js.map +1 -1
  44. package/cjs/utils/long-utils.d.ts +5 -0
  45. package/cjs/utils/long-utils.js +13 -1
  46. package/cjs/utils/long-utils.js.map +1 -1
  47. package/cjs/utils/sorted-array.js +1 -0
  48. package/cjs/utils/sorted-array.js.map +1 -1
  49. package/cjs/utils/type-assertions.d.ts +1 -0
  50. package/cjs/utils/type-assertions.js +7 -1
  51. package/cjs/utils/type-assertions.js.map +1 -1
  52. package/esm/base-client.d.ts +33 -166
  53. package/esm/base-client.js +70 -48
  54. package/esm/base-client.js.map +1 -1
  55. package/esm/base-client.types.d.ts +153 -0
  56. package/esm/base-client.types.js +2 -0
  57. package/esm/base-client.types.js.map +1 -0
  58. package/esm/index.d.ts +1 -0
  59. package/esm/index.js +1 -0
  60. package/esm/index.js.map +1 -1
  61. package/esm/network/mtproto-session.d.ts +10 -2
  62. package/esm/network/mtproto-session.js +34 -4
  63. package/esm/network/mtproto-session.js.map +1 -1
  64. package/esm/network/multi-session-connection.d.ts +1 -1
  65. package/esm/network/multi-session-connection.js +4 -3
  66. package/esm/network/multi-session-connection.js.map +1 -1
  67. package/esm/network/network-manager.d.ts +23 -0
  68. package/esm/network/network-manager.js +29 -9
  69. package/esm/network/network-manager.js.map +1 -1
  70. package/esm/network/server-salt.d.ts +12 -0
  71. package/esm/network/server-salt.js +38 -0
  72. package/esm/network/server-salt.js.map +1 -0
  73. package/esm/network/session-connection.d.ts +6 -1
  74. package/esm/network/session-connection.js +179 -54
  75. package/esm/network/session-connection.js.map +1 -1
  76. package/esm/storage/abstract.d.ts +12 -1
  77. package/esm/storage/abstract.js.map +1 -1
  78. package/esm/storage/idb.d.ts +3 -1
  79. package/esm/storage/idb.js +17 -0
  80. package/esm/storage/idb.js.map +1 -1
  81. package/esm/storage/index.d.ts +1 -0
  82. package/esm/storage/index.js +1 -0
  83. package/esm/storage/index.js.map +1 -1
  84. package/esm/storage/json.js +1 -0
  85. package/esm/storage/json.js.map +1 -1
  86. package/esm/storage/memory.d.ts +5 -2
  87. package/esm/storage/memory.js +16 -2
  88. package/esm/storage/memory.js.map +1 -1
  89. package/esm/storage/memory.test.js +2 -1
  90. package/esm/storage/memory.test.js.map +1 -1
  91. package/esm/utils/logger.js +8 -2
  92. package/esm/utils/logger.js.map +1 -1
  93. package/esm/utils/logger.test.js +12 -0
  94. package/esm/utils/logger.test.js.map +1 -1
  95. package/esm/utils/long-utils.d.ts +5 -0
  96. package/esm/utils/long-utils.js +11 -0
  97. package/esm/utils/long-utils.js.map +1 -1
  98. package/esm/utils/sorted-array.js +1 -0
  99. package/esm/utils/sorted-array.js.map +1 -1
  100. package/esm/utils/type-assertions.d.ts +1 -0
  101. package/esm/utils/type-assertions.js +5 -0
  102. package/esm/utils/type-assertions.js.map +1 -1
  103. package/package.json +4 -4
@@ -1,18 +1,19 @@
1
1
  import Long from 'long';
2
2
  import { TlSerializationCounter } from '@mtcute/tl-runtime';
3
3
  import { MtcuteError } from '../types/index.js';
4
- import { Deque, getRandomInt, LongMap, LruSet, randomLong, SortedArray, } from '../utils/index.js';
4
+ import { compareLongs, Deque, getRandomInt, LongMap, LruSet, randomLong, SortedArray, } from '../utils/index.js';
5
5
  import { AuthKey } from './auth-key.js';
6
6
  /**
7
7
  * Class encapsulating a single MTProto session and storing
8
8
  * all the relevant state
9
9
  */
10
10
  export class MtprotoSession {
11
- constructor(_crypto, log, _readerMap, _writerMap) {
11
+ constructor(_crypto, log, _readerMap, _writerMap, _salts) {
12
12
  this._crypto = _crypto;
13
13
  this.log = log;
14
14
  this._readerMap = _readerMap;
15
15
  this._writerMap = _writerMap;
16
+ this._salts = _salts;
16
17
  this._sessionId = randomLong();
17
18
  this._authKey = new AuthKey(this._crypto, this.log, this._readerMap);
18
19
  this._authKeyTemp = new AuthKey(this._crypto, this.log, this._readerMap);
@@ -20,7 +21,6 @@ export class MtprotoSession {
20
21
  this._timeOffset = 0;
21
22
  this._lastMessageId = Long.ZERO;
22
23
  this._seqNo = 0;
23
- this.serverSalt = Long.ZERO;
24
24
  /// state ///
25
25
  // recent msg ids
26
26
  this.recentOutgoingMsgIds = new LruSet(1000, true);
@@ -32,6 +32,8 @@ export class MtprotoSession {
32
32
  this.queuedResendReq = [];
33
33
  this.queuedCancelReq = [];
34
34
  this.getStateSchedule = new SortedArray([], (a, b) => a.getState - b.getState);
35
+ this.chains = new Map();
36
+ this.chainsPendingFails = new Map();
35
37
  // requests info
36
38
  this.pendingMessages = new LongMap();
37
39
  this.destroySessionIdToMsgId = new LongMap();
@@ -98,6 +100,7 @@ export class MtprotoSession {
98
100
  this.queuedStateReq.length = 0;
99
101
  this.queuedResendReq.length = 0;
100
102
  this.getStateSchedule.clear();
103
+ this.chains.clear();
101
104
  }
102
105
  enqueueRpc(rpc, force) {
103
106
  // already queued or cancelled
@@ -132,7 +135,7 @@ export class MtprotoSession {
132
135
  /** Encrypt a single MTProto message using session's keys */
133
136
  encryptMessage(message) {
134
137
  const key = this._authKeyTemp.ready ? this._authKeyTemp : this._authKey;
135
- return key.encryptMessage(message, this.serverSalt, this._sessionId);
138
+ return key.encryptMessage(message, this._salts.currentSalt, this._sessionId);
136
139
  }
137
140
  /** Decrypt a single MTProto message using session's keys */
138
141
  decryptMessage(data, callback) {
@@ -198,5 +201,32 @@ export class MtprotoSession {
198
201
  }
199
202
  this.lastPingMsgId = Long.ZERO;
200
203
  }
204
+ addToChain(chainId, msgId) {
205
+ const prevMsgId = this.chains.get(chainId);
206
+ this.chains.set(chainId, msgId);
207
+ this.log.debug('added message %l to chain %s (prev: %l)', msgId, chainId, prevMsgId);
208
+ return prevMsgId;
209
+ }
210
+ removeFromChain(chainId, msgId) {
211
+ const lastMsgId = this.chains.get(chainId);
212
+ if (!lastMsgId) {
213
+ this.log.warn('tried to remove message %l from empty chain %s', msgId, chainId);
214
+ return;
215
+ }
216
+ if (lastMsgId.eq(msgId)) {
217
+ // last message of the chain, remove it
218
+ this.log.debug('chain %s: exhausted, last message %l', msgId, chainId);
219
+ this.chains.delete(chainId);
220
+ }
221
+ // do nothing
222
+ }
223
+ getPendingChainedFails(chainId) {
224
+ let arr = this.chainsPendingFails.get(chainId);
225
+ if (!arr) {
226
+ arr = new SortedArray([], (a, b) => compareLongs(a.invokeAfter, b.invokeAfter));
227
+ this.chainsPendingFails.set(chainId, arr);
228
+ }
229
+ return arr;
230
+ }
201
231
  }
202
232
  //# sourceMappingURL=mtproto-session.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"mtproto-session.js","sourceRoot":"","sources":["../../../src/network/mtproto-session.ts"],"names":[],"mappings":"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","sourcesContent":["import Long from 'long'\n\nimport { mtp, tl } from '@mtcute/tl'\nimport { TlBinaryWriter, TlReaderMap, TlSerializationCounter, TlWriterMap } from '@mtcute/tl-runtime'\n\nimport { MtcuteError } from '../types/index.js'\nimport {\n ControllablePromise,\n Deque,\n getRandomInt,\n ICryptoProvider,\n Logger,\n LongMap,\n LruSet,\n randomLong,\n SortedArray,\n} from '../utils/index.js'\nimport { AuthKey } from './auth-key.js'\n\nexport interface PendingRpc {\n method: string\n data: Uint8Array\n promise: ControllablePromise\n stack?: string\n gzipOverhead?: number\n\n sent?: boolean\n done?: boolean\n msgId?: Long\n seqNo?: number\n containerId?: Long\n acked?: boolean\n initConn?: boolean\n getState?: number\n cancelled?: boolean\n timeout?: NodeJS.Timeout\n}\n\nexport type PendingMessage =\n | {\n _: 'rpc'\n rpc: PendingRpc\n }\n | {\n _: 'container'\n msgIds: Long[]\n }\n | {\n _: 'state'\n msgIds: Long[]\n containerId: Long\n }\n | {\n _: 'resend'\n msgIds: Long[]\n containerId: Long\n }\n | {\n _: 'ping'\n pingId: Long\n containerId: Long\n }\n | {\n _: 'destroy_session'\n sessionId: Long\n containerId: Long\n }\n | {\n _: 'cancel'\n msgId: Long\n containerId: Long\n }\n | {\n _: 'future_salts'\n containerId: Long\n }\n | {\n _: 'bind'\n promise: ControllablePromise\n }\n\n/**\n * Class encapsulating a single MTProto session and storing\n * all the relevant state\n */\nexport class MtprotoSession {\n _sessionId = randomLong()\n\n _authKey = new AuthKey(this._crypto, this.log, this._readerMap)\n _authKeyTemp = new AuthKey(this._crypto, this.log, this._readerMap)\n _authKeyTempSecondary = new AuthKey(this._crypto, this.log, this._readerMap)\n\n _timeOffset = 0\n _lastMessageId = Long.ZERO\n _seqNo = 0\n\n serverSalt = Long.ZERO\n\n /// state ///\n // recent msg ids\n recentOutgoingMsgIds = new LruSet<Long>(1000, true)\n recentIncomingMsgIds = new LruSet<Long>(1000, true)\n\n // queues\n queuedRpc = new Deque<PendingRpc>()\n queuedAcks: Long[] = []\n queuedStateReq: Long[] = []\n queuedResendReq: Long[] = []\n queuedCancelReq: Long[] = []\n getStateSchedule = new SortedArray<PendingRpc>([], (a, b) => a.getState! - b.getState!)\n\n // requests info\n pendingMessages = new LongMap<PendingMessage>()\n destroySessionIdToMsgId = new LongMap<Long>()\n\n lastPingRtt = NaN\n lastPingTime = 0\n lastPingMsgId = Long.ZERO\n lastSessionCreatedUid = Long.ZERO\n\n initConnectionCalled = false\n authorizationPending = false\n\n next429Timeout = 1000\n current429Timeout?: NodeJS.Timeout\n next429ResetTimeout?: NodeJS.Timeout\n\n constructor(\n readonly _crypto: ICryptoProvider,\n readonly log: Logger,\n readonly _readerMap: TlReaderMap,\n readonly _writerMap: TlWriterMap,\n ) {\n this.log.prefix = `[SESSION ${this._sessionId.toString(16)}] `\n }\n\n get hasPendingMessages(): boolean {\n return Boolean(\n this.queuedRpc.length ||\n this.queuedAcks.length ||\n this.queuedStateReq.length ||\n this.queuedResendReq.length,\n )\n }\n\n /**\n * Reset session by resetting auth key(s) and session state\n */\n reset(withAuthKey = false): void {\n if (withAuthKey) {\n this._authKey.reset()\n this._authKeyTemp.reset()\n this._authKeyTempSecondary.reset()\n }\n\n clearTimeout(this.current429Timeout)\n this.resetState()\n this.resetLastPing(true)\n }\n\n /**\n * Reset session state and generate a new session ID.\n *\n * By default, also cancels any pending RPC requests.\n * If `keepPending` is set to `true`, pending requests will be kept\n */\n resetState(keepPending = false): void {\n this._lastMessageId = Long.ZERO\n this._seqNo = 0\n\n this._sessionId = randomLong()\n this.log.debug('session reset, new sid = %h', this._sessionId)\n this.log.prefix = `[SESSION ${this._sessionId.toString(16)}] `\n\n // reset session state\n\n if (!keepPending) {\n for (const info of this.pendingMessages.values()) {\n if (info._ === 'rpc') {\n info.rpc.promise.reject(new MtcuteError('Session is reset'))\n }\n }\n this.pendingMessages.clear()\n }\n\n this.recentOutgoingMsgIds.clear()\n this.recentIncomingMsgIds.clear()\n\n if (!keepPending) {\n while (this.queuedRpc.length) {\n const rpc = this.queuedRpc.popFront()!\n\n if (rpc.sent === false) {\n rpc.promise.reject(new MtcuteError('Session is reset'))\n }\n }\n }\n\n this.queuedAcks.length = 0\n this.queuedStateReq.length = 0\n this.queuedResendReq.length = 0\n this.getStateSchedule.clear()\n }\n\n enqueueRpc(rpc: PendingRpc, force?: boolean): boolean {\n // already queued or cancelled\n if ((!force && !rpc.sent) || rpc.cancelled) return false\n\n rpc.sent = false\n rpc.containerId = undefined\n this.log.debug('enqueued %s for sending (msg_id = %s)', rpc.method, rpc.msgId || 'n/a')\n this.queuedRpc.pushBack(rpc)\n\n return true\n }\n\n getMessageId(): Long {\n const timeTicks = Date.now()\n const timeSec = Math.floor(timeTicks / 1000) + this._timeOffset\n const timeMSec = timeTicks % 1000\n const random = getRandomInt(0xffff)\n\n let messageId = new Long((timeMSec << 21) | (random << 3) | 4, timeSec)\n\n if (this._lastMessageId.gt(messageId)) {\n messageId = this._lastMessageId.add(4)\n }\n\n this._lastMessageId = messageId\n\n return messageId\n }\n\n getSeqNo(isContentRelated = true): number {\n let seqNo = this._seqNo\n\n if (isContentRelated) {\n seqNo += 1\n this._seqNo += 2\n }\n\n return seqNo\n }\n\n /** Encrypt a single MTProto message using session's keys */\n encryptMessage(message: Uint8Array): Uint8Array {\n const key = this._authKeyTemp.ready ? this._authKeyTemp : this._authKey\n\n return key.encryptMessage(message, this.serverSalt, this._sessionId)\n }\n\n /** Decrypt a single MTProto message using session's keys */\n decryptMessage(data: Uint8Array, callback: Parameters<AuthKey['decryptMessage']>[2]): void {\n if (!this._authKey.ready) throw new MtcuteError('Keys are not set up!')\n\n const authKeyId = data.subarray(0, 8)\n\n let key: AuthKey\n\n if (this._authKey.match(authKeyId)) {\n key = this._authKey\n } else if (this._authKeyTemp.match(authKeyId)) {\n key = this._authKeyTemp\n } else if (this._authKeyTempSecondary.match(authKeyId)) {\n key = this._authKeyTempSecondary\n } else {\n this.log.warn(\n 'received message with unknown authKey = %h (expected %h or %h or %h)',\n authKeyId,\n this._authKey.id,\n this._authKeyTemp.id,\n this._authKeyTempSecondary.id,\n )\n\n return\n }\n\n return key.decryptMessage(data, this._sessionId, callback)\n }\n\n writeMessage(\n writer: TlBinaryWriter,\n content: tl.TlObject | mtp.TlObject | Uint8Array,\n isContentRelated = true,\n ): Long {\n const messageId = this.getMessageId()\n const seqNo = this.getSeqNo(isContentRelated)\n\n const length = ArrayBuffer.isView(content) ?\n content.length :\n TlSerializationCounter.countNeededBytes(writer.objectMap!, content)\n\n writer.long(messageId)\n writer.int(seqNo)\n writer.uint(length)\n if (ArrayBuffer.isView(content)) writer.raw(content)\n else writer.object(content as tl.TlObject)\n\n return messageId\n }\n\n onTransportFlood(callback: () => void) {\n if (this.current429Timeout) return // already waiting\n\n // all active queries must be resent after a timeout\n this.resetLastPing(true)\n\n const timeout = this.next429Timeout\n\n this.next429Timeout = Math.min(this.next429Timeout * 2, 32000)\n clearTimeout(this.current429Timeout)\n clearTimeout(this.next429ResetTimeout)\n\n this.current429Timeout = setTimeout(() => {\n this.current429Timeout = undefined\n callback()\n }, timeout)\n this.next429ResetTimeout = setTimeout(() => {\n this.next429ResetTimeout = undefined\n this.next429Timeout = 1000\n }, 60000)\n\n this.log.debug('transport flood, waiting for %d ms before proceeding', timeout)\n\n return Date.now() + timeout\n }\n\n resetLastPing(withTime = false): void {\n if (withTime) this.lastPingTime = 0\n\n if (!this.lastPingMsgId.isZero()) {\n this.pendingMessages.delete(this.lastPingMsgId)\n }\n\n this.lastPingMsgId = Long.ZERO\n }\n}\n"]}
1
+ {"version":3,"file":"mtproto-session.js","sourceRoot":"","sources":["../../../src/network/mtproto-session.ts"],"names":[],"mappings":"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","sourcesContent":["import Long from 'long'\n\nimport { mtp, tl } from '@mtcute/tl'\nimport { TlBinaryWriter, TlReaderMap, TlSerializationCounter, TlWriterMap } from '@mtcute/tl-runtime'\n\nimport { MtcuteError } from '../types/index.js'\nimport {\n compareLongs,\n ControllablePromise,\n Deque,\n getRandomInt,\n ICryptoProvider,\n Logger,\n LongMap,\n LruSet,\n randomLong,\n SortedArray,\n} from '../utils/index.js'\nimport { AuthKey } from './auth-key.js'\nimport { ServerSaltManager } from './server-salt.js'\n\nexport interface PendingRpc {\n method: string\n data: Uint8Array\n promise: ControllablePromise\n stack?: string\n gzipOverhead?: number\n\n chainId?: string | number\n invokeAfter?: Long\n\n sent?: boolean\n done?: boolean\n msgId?: Long\n seqNo?: number\n containerId?: Long\n acked?: boolean\n initConn?: boolean\n getState?: number\n cancelled?: boolean\n timeout?: NodeJS.Timeout\n}\n\nexport type PendingMessage =\n | {\n _: 'rpc'\n rpc: PendingRpc\n }\n | {\n _: 'container'\n msgIds: Long[]\n }\n | {\n _: 'state'\n msgIds: Long[]\n containerId: Long\n }\n | {\n _: 'resend'\n msgIds: Long[]\n containerId: Long\n }\n | {\n _: 'ping'\n pingId: Long\n containerId: Long\n }\n | {\n _: 'destroy_session'\n sessionId: Long\n containerId: Long\n }\n | {\n _: 'cancel'\n msgId: Long\n containerId: Long\n }\n | {\n _: 'future_salts'\n containerId: Long\n }\n | {\n _: 'bind'\n promise: ControllablePromise\n }\n\n/**\n * Class encapsulating a single MTProto session and storing\n * all the relevant state\n */\nexport class MtprotoSession {\n _sessionId = randomLong()\n\n _authKey = new AuthKey(this._crypto, this.log, this._readerMap)\n _authKeyTemp = new AuthKey(this._crypto, this.log, this._readerMap)\n _authKeyTempSecondary = new AuthKey(this._crypto, this.log, this._readerMap)\n\n _timeOffset = 0\n _lastMessageId = Long.ZERO\n _seqNo = 0\n\n /// state ///\n // recent msg ids\n recentOutgoingMsgIds = new LruSet<Long>(1000, true)\n recentIncomingMsgIds = new LruSet<Long>(1000, true)\n\n // queues\n queuedRpc = new Deque<PendingRpc>()\n queuedAcks: Long[] = []\n queuedStateReq: Long[] = []\n queuedResendReq: Long[] = []\n queuedCancelReq: Long[] = []\n getStateSchedule = new SortedArray<PendingRpc>([], (a, b) => a.getState! - b.getState!)\n\n chains = new Map<string | number, Long>()\n chainsPendingFails = new Map<string | number, SortedArray<PendingRpc>>()\n\n // requests info\n pendingMessages = new LongMap<PendingMessage>()\n destroySessionIdToMsgId = new LongMap<Long>()\n\n lastPingRtt = NaN\n lastPingTime = 0\n lastPingMsgId = Long.ZERO\n lastSessionCreatedUid = Long.ZERO\n\n initConnectionCalled = false\n authorizationPending = false\n\n next429Timeout = 1000\n current429Timeout?: NodeJS.Timeout\n next429ResetTimeout?: NodeJS.Timeout\n\n constructor(\n readonly _crypto: ICryptoProvider,\n readonly log: Logger,\n readonly _readerMap: TlReaderMap,\n readonly _writerMap: TlWriterMap,\n readonly _salts: ServerSaltManager,\n ) {\n this.log.prefix = `[SESSION ${this._sessionId.toString(16)}] `\n }\n\n get hasPendingMessages(): boolean {\n return Boolean(\n this.queuedRpc.length ||\n this.queuedAcks.length ||\n this.queuedStateReq.length ||\n this.queuedResendReq.length,\n )\n }\n\n /**\n * Reset session by resetting auth key(s) and session state\n */\n reset(withAuthKey = false): void {\n if (withAuthKey) {\n this._authKey.reset()\n this._authKeyTemp.reset()\n this._authKeyTempSecondary.reset()\n }\n\n clearTimeout(this.current429Timeout)\n this.resetState()\n this.resetLastPing(true)\n }\n\n /**\n * Reset session state and generate a new session ID.\n *\n * By default, also cancels any pending RPC requests.\n * If `keepPending` is set to `true`, pending requests will be kept\n */\n resetState(keepPending = false): void {\n this._lastMessageId = Long.ZERO\n this._seqNo = 0\n\n this._sessionId = randomLong()\n this.log.debug('session reset, new sid = %h', this._sessionId)\n this.log.prefix = `[SESSION ${this._sessionId.toString(16)}] `\n\n // reset session state\n\n if (!keepPending) {\n for (const info of this.pendingMessages.values()) {\n if (info._ === 'rpc') {\n info.rpc.promise.reject(new MtcuteError('Session is reset'))\n }\n }\n this.pendingMessages.clear()\n }\n\n this.recentOutgoingMsgIds.clear()\n this.recentIncomingMsgIds.clear()\n\n if (!keepPending) {\n while (this.queuedRpc.length) {\n const rpc = this.queuedRpc.popFront()!\n\n if (rpc.sent === false) {\n rpc.promise.reject(new MtcuteError('Session is reset'))\n }\n }\n }\n\n this.queuedAcks.length = 0\n this.queuedStateReq.length = 0\n this.queuedResendReq.length = 0\n this.getStateSchedule.clear()\n this.chains.clear()\n }\n\n enqueueRpc(rpc: PendingRpc, force?: boolean): boolean {\n // already queued or cancelled\n if ((!force && !rpc.sent) || rpc.cancelled) return false\n\n rpc.sent = false\n rpc.containerId = undefined\n this.log.debug('enqueued %s for sending (msg_id = %s)', rpc.method, rpc.msgId || 'n/a')\n this.queuedRpc.pushBack(rpc)\n\n return true\n }\n\n getMessageId(): Long {\n const timeTicks = Date.now()\n const timeSec = Math.floor(timeTicks / 1000) + this._timeOffset\n const timeMSec = timeTicks % 1000\n const random = getRandomInt(0xffff)\n\n let messageId = new Long((timeMSec << 21) | (random << 3) | 4, timeSec)\n\n if (this._lastMessageId.gt(messageId)) {\n messageId = this._lastMessageId.add(4)\n }\n\n this._lastMessageId = messageId\n\n return messageId\n }\n\n getSeqNo(isContentRelated = true): number {\n let seqNo = this._seqNo\n\n if (isContentRelated) {\n seqNo += 1\n this._seqNo += 2\n }\n\n return seqNo\n }\n\n /** Encrypt a single MTProto message using session's keys */\n encryptMessage(message: Uint8Array): Uint8Array {\n const key = this._authKeyTemp.ready ? this._authKeyTemp : this._authKey\n\n return key.encryptMessage(message, this._salts.currentSalt, this._sessionId)\n }\n\n /** Decrypt a single MTProto message using session's keys */\n decryptMessage(data: Uint8Array, callback: Parameters<AuthKey['decryptMessage']>[2]): void {\n if (!this._authKey.ready) throw new MtcuteError('Keys are not set up!')\n\n const authKeyId = data.subarray(0, 8)\n\n let key: AuthKey\n\n if (this._authKey.match(authKeyId)) {\n key = this._authKey\n } else if (this._authKeyTemp.match(authKeyId)) {\n key = this._authKeyTemp\n } else if (this._authKeyTempSecondary.match(authKeyId)) {\n key = this._authKeyTempSecondary\n } else {\n this.log.warn(\n 'received message with unknown authKey = %h (expected %h or %h or %h)',\n authKeyId,\n this._authKey.id,\n this._authKeyTemp.id,\n this._authKeyTempSecondary.id,\n )\n\n return\n }\n\n return key.decryptMessage(data, this._sessionId, callback)\n }\n\n writeMessage(\n writer: TlBinaryWriter,\n content: tl.TlObject | mtp.TlObject | Uint8Array,\n isContentRelated = true,\n ): Long {\n const messageId = this.getMessageId()\n const seqNo = this.getSeqNo(isContentRelated)\n\n const length = ArrayBuffer.isView(content) ?\n content.length :\n TlSerializationCounter.countNeededBytes(writer.objectMap!, content)\n\n writer.long(messageId)\n writer.int(seqNo)\n writer.uint(length)\n if (ArrayBuffer.isView(content)) writer.raw(content)\n else writer.object(content as tl.TlObject)\n\n return messageId\n }\n\n onTransportFlood(callback: () => void) {\n if (this.current429Timeout) return // already waiting\n\n // all active queries must be resent after a timeout\n this.resetLastPing(true)\n\n const timeout = this.next429Timeout\n\n this.next429Timeout = Math.min(this.next429Timeout * 2, 32000)\n clearTimeout(this.current429Timeout)\n clearTimeout(this.next429ResetTimeout)\n\n this.current429Timeout = setTimeout(() => {\n this.current429Timeout = undefined\n callback()\n }, timeout)\n this.next429ResetTimeout = setTimeout(() => {\n this.next429ResetTimeout = undefined\n this.next429Timeout = 1000\n }, 60000)\n\n this.log.debug('transport flood, waiting for %d ms before proceeding', timeout)\n\n return Date.now() + timeout\n }\n\n resetLastPing(withTime = false): void {\n if (withTime) this.lastPingTime = 0\n\n if (!this.lastPingMsgId.isZero()) {\n this.pendingMessages.delete(this.lastPingMsgId)\n }\n\n this.lastPingMsgId = Long.ZERO\n }\n\n addToChain(chainId: string | number, msgId: Long): Long | undefined {\n const prevMsgId = this.chains.get(chainId)\n this.chains.set(chainId, msgId)\n\n this.log.debug('added message %l to chain %s (prev: %l)', msgId, chainId, prevMsgId)\n\n return prevMsgId\n }\n\n removeFromChain(chainId: string | number, msgId: Long): void {\n const lastMsgId = this.chains.get(chainId)\n\n if (!lastMsgId) {\n this.log.warn('tried to remove message %l from empty chain %s', msgId, chainId)\n\n return\n }\n\n if (lastMsgId.eq(msgId)) {\n // last message of the chain, remove it\n this.log.debug('chain %s: exhausted, last message %l', msgId, chainId)\n this.chains.delete(chainId)\n }\n\n // do nothing\n }\n\n getPendingChainedFails(chainId: string | number): SortedArray<PendingRpc> {\n let arr = this.chainsPendingFails.get(chainId)\n\n if (!arr) {\n arr = new SortedArray<PendingRpc>([], (a, b) => compareLongs(a.invokeAfter!, b.invokeAfter!))\n this.chainsPendingFails.set(chainId, arr)\n }\n\n return arr\n }\n}\n"]}
@@ -19,7 +19,7 @@ export declare class MultiSessionConnection extends EventEmitter {
19
19
  _destroyed: boolean;
20
20
  destroy(): void;
21
21
  private _nextConnection;
22
- sendRpc<T extends tl.RpcMethod>(request: T, stack?: string, timeout?: number, abortSignal?: AbortSignal): Promise<tl.RpcCallReturn[T['_']]>;
22
+ sendRpc<T extends tl.RpcMethod>(request: T, stack?: string, timeout?: number, abortSignal?: AbortSignal, chainId?: string | number): Promise<tl.RpcCallReturn[T['_']]>;
23
23
  connect(): void;
24
24
  ensureConnected(): void;
25
25
  setAuthKey(authKey: Uint8Array | null, temp?: boolean, idx?: number): void;
@@ -65,7 +65,7 @@ export class MultiSessionConnection extends EventEmitter {
65
65
  }
66
66
  while (this._sessions.length < this._count) {
67
67
  const idx = this._sessions.length;
68
- const session = new MtprotoSession(this.params.crypto, this._log.create('session'), this.params.readerMap, this.params.writerMap);
68
+ const session = new MtprotoSession(this.params.crypto, this._log.create('session'), this.params.readerMap, this.params.writerMap, this.params.salts);
69
69
  // brvh
70
70
  if (idx !== 0)
71
71
  session._authKey = this._sessions[0]._authKey;
@@ -132,6 +132,7 @@ export class MultiSessionConnection extends EventEmitter {
132
132
  }
133
133
  });
134
134
  conn.on('tmp-key-change', (key, expires) => this.emit('tmp-key-change', i, key, expires));
135
+ conn.on('future-salts', (salts) => this.emit('future-salts', salts));
135
136
  conn.on('auth-begin', () => {
136
137
  this._log.debug('received auth-begin from connection %d', i);
137
138
  this.emit('auth-begin', i);
@@ -160,7 +161,7 @@ export class MultiSessionConnection extends EventEmitter {
160
161
  this.removeAllListeners();
161
162
  this._destroyed = true;
162
163
  }
163
- sendRpc(request, stack, timeout, abortSignal) {
164
+ sendRpc(request, stack, timeout, abortSignal, chainId) {
164
165
  // if (this.params.isMainConnection) {
165
166
  // find the least loaded connection
166
167
  let min = Infinity;
@@ -173,7 +174,7 @@ export class MultiSessionConnection extends EventEmitter {
173
174
  minIdx = i;
174
175
  }
175
176
  }
176
- return this._connections[minIdx].sendRpc(request, stack, timeout, abortSignal);
177
+ return this._connections[minIdx].sendRpc(request, stack, timeout, abortSignal, chainId);
177
178
  // }
178
179
  // round-robin connections
179
180
  // since they all share the same session, it doesn't matter which one we use
@@ -1 +1 @@
1
- {"version":3,"file":"multi-session-connection.js","sourceRoot":"","sources":["../../../src/network/multi-session-connection.ts"],"names":[],"mappings":"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","sourcesContent":["import EventEmitter from 'events'\n\nimport { tl } from '@mtcute/tl'\n\nimport { createControllablePromise, Logger } from '../utils/index.js'\nimport { MtprotoSession } from './mtproto-session.js'\nimport { SessionConnection, SessionConnectionParams } from './session-connection.js'\nimport { TransportFactory } from './transports/index.js'\n\nexport class MultiSessionConnection extends EventEmitter {\n private _log: Logger\n readonly _sessions: MtprotoSession[]\n private _enforcePfs = false\n\n constructor(\n readonly params: SessionConnectionParams,\n private _count: number,\n log: Logger,\n logPrefix = '',\n ) {\n super()\n this._log = log.create('multi')\n if (logPrefix) this._log.prefix = `[${logPrefix}] `\n this._enforcePfs = _count > 1 && params.isMainConnection\n\n this._sessions = []\n this._updateConnections()\n }\n\n protected _connections: SessionConnection[] = []\n\n setCount(count: number, connect = this.params.isMainConnection): void {\n this._count = count\n\n this._updateConnections(connect)\n }\n\n private _updateSessions(): void {\n // there are two cases\n // 1. this msc is main, in which case every connection should have its own session\n // 2. this msc is not main, in which case all connections should share the same session\n // if (!this.params.isMainConnection) {\n // // case 2\n // this._log.debug(\n // 'updating sessions count: %d -> 1',\n // this._sessions.length,\n // )\n //\n // if (this._sessions.length === 0) {\n // this._sessions.push(\n // new MtprotoSession(\n // this.params.crypto,\n // this._log.create('session'),\n // this.params.readerMap,\n // this.params.writerMap,\n // ),\n // )\n // }\n //\n // // shouldn't happen, but just in case\n // while (this._sessions.length > 1) {\n // this._sessions.pop()!.reset()\n // }\n //\n // return\n // }\n\n this._log.debug('updating sessions count: %d -> %d', this._sessions.length, this._count)\n\n // case 1\n if (this._sessions.length === this._count) return\n\n if (this._sessions.length > this._count) {\n // destroy extra sessions\n for (let i = this._sessions.length - 1; i >= this._count; i--) {\n this._sessions[i].reset()\n }\n\n this._sessions.splice(this._count)\n\n return\n }\n\n while (this._sessions.length < this._count) {\n const idx = this._sessions.length\n const session = new MtprotoSession(\n this.params.crypto,\n this._log.create('session'),\n this.params.readerMap,\n this.params.writerMap,\n )\n\n // brvh\n if (idx !== 0) session._authKey = this._sessions[0]._authKey\n\n this._sessions.push(session)\n }\n }\n\n private _updateConnections(connect = false): void {\n this._updateSessions()\n if (this._connections.length === this._count) return\n\n this._log.debug('updating connections count: %d -> %d', this._connections.length, this._count)\n\n const newEnforcePfs = this._count > 1 && this.params.isMainConnection\n const enforcePfsChanged = newEnforcePfs !== this._enforcePfs\n\n if (enforcePfsChanged) {\n this._log.debug('enforcePfs changed: %s -> %s', this._enforcePfs, newEnforcePfs)\n this._enforcePfs = newEnforcePfs\n }\n\n if (this._connections.length > this._count) {\n // destroy extra connections\n for (let i = this._connections.length - 1; i >= this._count; i--) {\n this._connections[i].removeAllListeners()\n this._connections[i].destroy()\n }\n\n this._connections.splice(this._count)\n\n return\n }\n\n if (enforcePfsChanged) {\n // we need to fetch new auth keys first\n const promise = createControllablePromise<void>()\n this.emit('request-keys', promise)\n\n promise\n .then(() => {\n this._connections.forEach((conn) => {\n conn.setUsePfs(this.params.usePfs || this._enforcePfs)\n\n if (connect) conn.connect()\n })\n })\n .catch((err) => {\n this.emit('error', err)\n })\n }\n\n // create new connections\n for (let i = this._connections.length; i < this._count; i++) {\n const session = this._sessions[i] // this.params.isMainConnection ? // :\n // this._sessions[0]\n const conn = new SessionConnection(\n {\n ...this.params,\n usePfs: this.params.usePfs || this._enforcePfs,\n isMainConnection: this.params.isMainConnection && i === 0,\n withUpdates:\n this.params.isMainConnection && this.params.isMainDcConnection && !this.params.disableUpdates,\n },\n session,\n )\n\n if (this.params.isMainConnection && this.params.isMainDcConnection) {\n conn.on('update', (update) => this.emit('update', update))\n }\n conn.on('error', (err) => this.emit('error', err, conn))\n conn.on('key-change', (key) => {\n this.emit('key-change', i, key)\n\n // notify other connections\n for (const conn_ of this._connections) {\n if (conn_ === conn) continue\n conn_.onConnected()\n }\n })\n conn.on('tmp-key-change', (key, expires) => this.emit('tmp-key-change', i, key, expires))\n conn.on('auth-begin', () => {\n this._log.debug('received auth-begin from connection %d', i)\n this.emit('auth-begin', i)\n\n // we need to reset temp auth keys if there are any left\n\n this._connections.forEach((conn_) => {\n conn_._session._authKeyTemp.reset()\n if (conn_ !== conn) conn_.reconnect()\n })\n })\n conn.on('usable', () => this.emit('usable', i))\n conn.on('request-auth', () => this.emit('request-auth', i))\n conn.on('flood-done', () => {\n this._log.debug('received flood-done from connection %d', i)\n\n this._connections.forEach((it) => it.flushWhenIdle())\n })\n\n this._connections.push(conn)\n // if enforcePfsChanged, we need to connect after setting the new auth key\n if (connect && !enforcePfsChanged) conn.connect()\n }\n }\n\n _destroyed = false\n destroy(): void {\n this._connections.forEach((conn) => conn.destroy())\n this._sessions.forEach((sess) => sess.reset())\n this.removeAllListeners()\n\n this._destroyed = true\n }\n\n private _nextConnection = 0\n\n sendRpc<T extends tl.RpcMethod>(\n request: T,\n stack?: string,\n timeout?: number,\n abortSignal?: AbortSignal,\n ): Promise<tl.RpcCallReturn[T['_']]> {\n // if (this.params.isMainConnection) {\n // find the least loaded connection\n let min = Infinity\n let minIdx = 0\n\n for (let i = 0; i < this._connections.length; i++) {\n const conn = this._connections[i]\n const total = conn._session.queuedRpc.length + conn._session.pendingMessages.size()\n\n if (total < min) {\n min = total\n minIdx = i\n }\n }\n\n return this._connections[minIdx].sendRpc(request, stack, timeout, abortSignal)\n // }\n\n // round-robin connections\n // since they all share the same session, it doesn't matter which one we use\n // the connection chosen here will only affect the first attempt at sending\n // return this._connections[\n // this._nextConnection++ % this._connections.length\n // ].sendRpc(request, stack, timeout)\n }\n\n connect(): void {\n for (const conn of this._connections) {\n conn.connect()\n }\n }\n\n ensureConnected(): void {\n if (this._connections[0].isConnected) return\n\n this.connect()\n }\n\n setAuthKey(authKey: Uint8Array | null, temp = false, idx = 0): void {\n const session = this._sessions[idx]\n const key = temp ? session._authKeyTemp : session._authKey\n key.setup(authKey)\n }\n\n resetAuthKeys(): void {\n for (const session of this._sessions) {\n session.reset(true)\n }\n this.notifyKeyChange()\n }\n\n setInactivityTimeout(timeout?: number): void {\n this._log.debug('setting inactivity timeout to %s', timeout)\n\n // for future connections (if any)\n this.params.inactivityTimeout = timeout\n\n // for current connections\n for (const conn of this._connections) {\n conn.setInactivityTimeout(timeout)\n }\n }\n\n notifyKeyChange(): void {\n // only expected to be called on non-main connections\n const session = this._sessions[0]\n\n if (this.params.usePfs && !session._authKeyTemp.ready) {\n this._log.debug('temp auth key needed but not ready, ignoring key change')\n\n return\n }\n\n if (this._sessions[0].queuedRpc.length) {\n // there are pending requests, we need to reconnect.\n this._log.debug('notifying key change on the connection due to queued rpc')\n this._connections[0].onConnected()\n }\n\n // connection is idle, we don't need to notify it\n }\n\n requestAuth(): void {\n this._connections[0]._authorize()\n }\n\n resetSessions(): void {\n if (this.params.isMainConnection) {\n for (const conn of this._connections) {\n conn._resetSession()\n }\n } else {\n this._connections[0]._resetSession()\n }\n }\n\n changeTransport(factory: TransportFactory): void {\n this._connections.forEach((conn) => conn.changeTransport(factory))\n }\n\n getPoolSize(): number {\n return this._connections.length\n }\n}\n"]}
1
+ {"version":3,"file":"multi-session-connection.js","sourceRoot":"","sources":["../../../src/network/multi-session-connection.ts"],"names":[],"mappings":"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","sourcesContent":["import EventEmitter from 'events'\n\nimport { tl } from '@mtcute/tl'\n\nimport { createControllablePromise, Logger } from '../utils/index.js'\nimport { MtprotoSession } from './mtproto-session.js'\nimport { SessionConnection, SessionConnectionParams } from './session-connection.js'\nimport { TransportFactory } from './transports/index.js'\n\nexport class MultiSessionConnection extends EventEmitter {\n private _log: Logger\n readonly _sessions: MtprotoSession[]\n private _enforcePfs = false\n\n constructor(\n readonly params: SessionConnectionParams,\n private _count: number,\n log: Logger,\n logPrefix = '',\n ) {\n super()\n this._log = log.create('multi')\n if (logPrefix) this._log.prefix = `[${logPrefix}] `\n this._enforcePfs = _count > 1 && params.isMainConnection\n\n this._sessions = []\n this._updateConnections()\n }\n\n protected _connections: SessionConnection[] = []\n\n setCount(count: number, connect = this.params.isMainConnection): void {\n this._count = count\n\n this._updateConnections(connect)\n }\n\n private _updateSessions(): void {\n // there are two cases\n // 1. this msc is main, in which case every connection should have its own session\n // 2. this msc is not main, in which case all connections should share the same session\n // if (!this.params.isMainConnection) {\n // // case 2\n // this._log.debug(\n // 'updating sessions count: %d -> 1',\n // this._sessions.length,\n // )\n //\n // if (this._sessions.length === 0) {\n // this._sessions.push(\n // new MtprotoSession(\n // this.params.crypto,\n // this._log.create('session'),\n // this.params.readerMap,\n // this.params.writerMap,\n // ),\n // )\n // }\n //\n // // shouldn't happen, but just in case\n // while (this._sessions.length > 1) {\n // this._sessions.pop()!.reset()\n // }\n //\n // return\n // }\n\n this._log.debug('updating sessions count: %d -> %d', this._sessions.length, this._count)\n\n // case 1\n if (this._sessions.length === this._count) return\n\n if (this._sessions.length > this._count) {\n // destroy extra sessions\n for (let i = this._sessions.length - 1; i >= this._count; i--) {\n this._sessions[i].reset()\n }\n\n this._sessions.splice(this._count)\n\n return\n }\n\n while (this._sessions.length < this._count) {\n const idx = this._sessions.length\n const session = new MtprotoSession(\n this.params.crypto,\n this._log.create('session'),\n this.params.readerMap,\n this.params.writerMap,\n this.params.salts,\n )\n\n // brvh\n if (idx !== 0) session._authKey = this._sessions[0]._authKey\n\n this._sessions.push(session)\n }\n }\n\n private _updateConnections(connect = false): void {\n this._updateSessions()\n if (this._connections.length === this._count) return\n\n this._log.debug('updating connections count: %d -> %d', this._connections.length, this._count)\n\n const newEnforcePfs = this._count > 1 && this.params.isMainConnection\n const enforcePfsChanged = newEnforcePfs !== this._enforcePfs\n\n if (enforcePfsChanged) {\n this._log.debug('enforcePfs changed: %s -> %s', this._enforcePfs, newEnforcePfs)\n this._enforcePfs = newEnforcePfs\n }\n\n if (this._connections.length > this._count) {\n // destroy extra connections\n for (let i = this._connections.length - 1; i >= this._count; i--) {\n this._connections[i].removeAllListeners()\n this._connections[i].destroy()\n }\n\n this._connections.splice(this._count)\n\n return\n }\n\n if (enforcePfsChanged) {\n // we need to fetch new auth keys first\n const promise = createControllablePromise<void>()\n this.emit('request-keys', promise)\n\n promise\n .then(() => {\n this._connections.forEach((conn) => {\n conn.setUsePfs(this.params.usePfs || this._enforcePfs)\n\n if (connect) conn.connect()\n })\n })\n .catch((err) => {\n this.emit('error', err)\n })\n }\n\n // create new connections\n for (let i = this._connections.length; i < this._count; i++) {\n const session = this._sessions[i] // this.params.isMainConnection ? // :\n // this._sessions[0]\n const conn = new SessionConnection(\n {\n ...this.params,\n usePfs: this.params.usePfs || this._enforcePfs,\n isMainConnection: this.params.isMainConnection && i === 0,\n withUpdates:\n this.params.isMainConnection && this.params.isMainDcConnection && !this.params.disableUpdates,\n },\n session,\n )\n\n if (this.params.isMainConnection && this.params.isMainDcConnection) {\n conn.on('update', (update) => this.emit('update', update))\n }\n conn.on('error', (err) => this.emit('error', err, conn))\n conn.on('key-change', (key) => {\n this.emit('key-change', i, key)\n\n // notify other connections\n for (const conn_ of this._connections) {\n if (conn_ === conn) continue\n conn_.onConnected()\n }\n })\n conn.on('tmp-key-change', (key, expires) => this.emit('tmp-key-change', i, key, expires))\n conn.on('future-salts', (salts) => this.emit('future-salts', salts))\n conn.on('auth-begin', () => {\n this._log.debug('received auth-begin from connection %d', i)\n this.emit('auth-begin', i)\n\n // we need to reset temp auth keys if there are any left\n\n this._connections.forEach((conn_) => {\n conn_._session._authKeyTemp.reset()\n if (conn_ !== conn) conn_.reconnect()\n })\n })\n conn.on('usable', () => this.emit('usable', i))\n conn.on('request-auth', () => this.emit('request-auth', i))\n conn.on('flood-done', () => {\n this._log.debug('received flood-done from connection %d', i)\n\n this._connections.forEach((it) => it.flushWhenIdle())\n })\n\n this._connections.push(conn)\n // if enforcePfsChanged, we need to connect after setting the new auth key\n if (connect && !enforcePfsChanged) conn.connect()\n }\n }\n\n _destroyed = false\n destroy(): void {\n this._connections.forEach((conn) => conn.destroy())\n this._sessions.forEach((sess) => sess.reset())\n this.removeAllListeners()\n\n this._destroyed = true\n }\n\n private _nextConnection = 0\n\n sendRpc<T extends tl.RpcMethod>(\n request: T,\n stack?: string,\n timeout?: number,\n abortSignal?: AbortSignal,\n chainId?: string | number,\n ): Promise<tl.RpcCallReturn[T['_']]> {\n // if (this.params.isMainConnection) {\n // find the least loaded connection\n let min = Infinity\n let minIdx = 0\n\n for (let i = 0; i < this._connections.length; i++) {\n const conn = this._connections[i]\n const total = conn._session.queuedRpc.length + conn._session.pendingMessages.size()\n\n if (total < min) {\n min = total\n minIdx = i\n }\n }\n\n return this._connections[minIdx].sendRpc(request, stack, timeout, abortSignal, chainId)\n // }\n\n // round-robin connections\n // since they all share the same session, it doesn't matter which one we use\n // the connection chosen here will only affect the first attempt at sending\n // return this._connections[\n // this._nextConnection++ % this._connections.length\n // ].sendRpc(request, stack, timeout)\n }\n\n connect(): void {\n for (const conn of this._connections) {\n conn.connect()\n }\n }\n\n ensureConnected(): void {\n if (this._connections[0].isConnected) return\n\n this.connect()\n }\n\n setAuthKey(authKey: Uint8Array | null, temp = false, idx = 0): void {\n const session = this._sessions[idx]\n const key = temp ? session._authKeyTemp : session._authKey\n key.setup(authKey)\n }\n\n resetAuthKeys(): void {\n for (const session of this._sessions) {\n session.reset(true)\n }\n this.notifyKeyChange()\n }\n\n setInactivityTimeout(timeout?: number): void {\n this._log.debug('setting inactivity timeout to %s', timeout)\n\n // for future connections (if any)\n this.params.inactivityTimeout = timeout\n\n // for current connections\n for (const conn of this._connections) {\n conn.setInactivityTimeout(timeout)\n }\n }\n\n notifyKeyChange(): void {\n // only expected to be called on non-main connections\n const session = this._sessions[0]\n\n if (this.params.usePfs && !session._authKeyTemp.ready) {\n this._log.debug('temp auth key needed but not ready, ignoring key change')\n\n return\n }\n\n if (this._sessions[0].queuedRpc.length) {\n // there are pending requests, we need to reconnect.\n this._log.debug('notifying key change on the connection due to queued rpc')\n this._connections[0].onConnected()\n }\n\n // connection is idle, we don't need to notify it\n }\n\n requestAuth(): void {\n this._connections[0]._authorize()\n }\n\n resetSessions(): void {\n if (this.params.isMainConnection) {\n for (const conn of this._connections) {\n conn._resetSession()\n }\n } else {\n this._connections[0]._resetSession()\n }\n }\n\n changeTransport(factory: TransportFactory): void {\n this._connections.forEach((conn) => conn.changeTransport(factory))\n }\n\n getPoolSize(): number {\n return this._connections.length\n }\n}\n"]}
@@ -89,16 +89,22 @@ export interface RpcCallOptions {
89
89
  */
90
90
  timeout?: number;
91
91
  /**
92
+ * **ADVANCED**
93
+ *
92
94
  * Kind of connection to use for this call.
93
95
  *
94
96
  * @default 'main'
95
97
  */
96
98
  kind?: ConnectionKind;
97
99
  /**
100
+ * **ADVANCED**
101
+ *
98
102
  * ID of the DC to use for this call
99
103
  */
100
104
  dcId?: number;
101
105
  /**
106
+ * **ADVANCED**
107
+ *
102
108
  * DC connection manager to use for this call.
103
109
  * Overrides `dcId` if set.
104
110
  */
@@ -107,6 +113,21 @@ export interface RpcCallOptions {
107
113
  * Abort signal for the call.
108
114
  */
109
115
  abortSignal?: AbortSignal;
116
+ /**
117
+ * Whether we should not retry on -503 errors and throw {@link MtTimeoutError} immediately instead.
118
+ *
119
+ * Useful for methods like `messages.getBotCallbackAnswer` that reliably return
120
+ * -503 in case the upstream bot failed to respond.
121
+ */
122
+ throw503?: boolean;
123
+ /**
124
+ * Some requests should be processed consecutively, and not in parallel.
125
+ * Using the same `chainId` for multiple requests will ensure that they are processed in the order
126
+ * of calling `.call()`.
127
+ *
128
+ * Particularly useful for `messages.sendMessage` and alike.
129
+ */
130
+ chainId?: string | number;
110
131
  }
111
132
  /**
112
133
  * Wrapper over all connection pools for a single DC.
@@ -120,6 +141,7 @@ export declare class DcConnectionManager {
120
141
  readonly _dcs: ITelegramStorage.DcOptions;
121
142
  /** Whether this DC is the primary one */
122
143
  isPrimary: boolean;
144
+ private _salts;
123
145
  private __baseConnectionParams;
124
146
  private _log;
125
147
  /** Main connection pool */
@@ -144,6 +166,7 @@ export declare class DcConnectionManager {
144
166
  setIsPrimary(isPrimary: boolean): void;
145
167
  setIsPremium(isPremium: boolean): void;
146
168
  loadKeys(forcePfs?: boolean): Promise<boolean>;
169
+ destroy(): void;
147
170
  }
148
171
  /**
149
172
  * Class that manages all connections to Telegram servers.
@@ -1,9 +1,10 @@
1
1
  import { tl } from '@mtcute/tl';
2
- import { MtArgumentError, MtcuteError } from '../types/index.js';
2
+ import { MtArgumentError, MtcuteError, MtTimeoutError } from '../types/index.js';
3
3
  import { createControllablePromise, sleep } from '../utils/index.js';
4
4
  import { assertTypeIs } from '../utils/type-assertions.js';
5
5
  import { MultiSessionConnection } from './multi-session-connection.js';
6
6
  import { defaultReconnectionStrategy } from './reconnection.js';
7
+ import { ServerSaltManager } from './server-salt.js';
7
8
  import { defaultTransportFactory } from './transports/index.js';
8
9
  const CLIENT_ERRORS = {
9
10
  [tl.RpcError.BAD_REQUEST]: 1,
@@ -48,6 +49,7 @@ export class DcConnectionManager {
48
49
  this.dcId = dcId;
49
50
  this._dcs = _dcs;
50
51
  this.isPrimary = isPrimary;
52
+ this._salts = new ServerSaltManager();
51
53
  this.__baseConnectionParams = () => ({
52
54
  crypto: this.manager.params.crypto,
53
55
  initConnection: this.manager._initConnectionParams,
@@ -64,6 +66,7 @@ export class DcConnectionManager {
64
66
  isMainDcConnection: this.isPrimary,
65
67
  inactivityTimeout: this.manager.params.inactivityTimeout ?? 60000,
66
68
  enableErrorReporting: this.manager.params.enableErrorReporting,
69
+ salts: this._salts,
67
70
  });
68
71
  this._log = this.manager._log.create('dc-manager');
69
72
  /** Upload connection pool */
@@ -125,6 +128,9 @@ export class DcConnectionManager {
125
128
  })
126
129
  .catch((e) => this.manager.params._emitError(e));
127
130
  });
131
+ connection.on('future-salts', (salts) => {
132
+ Promise.resolve(this.manager._storage.setFutureSalts(this.dcId, salts)).catch((e) => this.manager.params._emitError(e));
133
+ });
128
134
  connection.on('auth-begin', () => {
129
135
  // we need to propagate auth-begin to all connections
130
136
  // to avoid them sending requests before auth is complete
@@ -169,11 +175,17 @@ export class DcConnectionManager {
169
175
  this.downloadSmall.setCount(this.manager._connectionCount('downloadSmall', this.dcId, isPremium));
170
176
  }
171
177
  async loadKeys(forcePfs = false) {
172
- const permanent = await this.manager._storage.getAuthKeyFor(this.dcId);
178
+ const [permanent, salts] = await Promise.all([
179
+ this.manager._storage.getAuthKeyFor(this.dcId),
180
+ this.manager._storage.getFutureSalts(this.dcId),
181
+ ]);
173
182
  this.main.setAuthKey(permanent);
174
183
  this.upload.setAuthKey(permanent);
175
184
  this.download.setAuthKey(permanent);
176
185
  this.downloadSmall.setAuthKey(permanent);
186
+ if (salts) {
187
+ this._salts.setFutureSalts(salts);
188
+ }
177
189
  if (!permanent) {
178
190
  return false;
179
191
  }
@@ -190,6 +202,13 @@ export class DcConnectionManager {
190
202
  }
191
203
  return true;
192
204
  }
205
+ destroy() {
206
+ this.main.destroy();
207
+ this.upload.destroy();
208
+ this.download.destroy();
209
+ this.downloadSmall.destroy();
210
+ this._salts.destroy();
211
+ }
193
212
  }
194
213
  /**
195
214
  * Class that manages all connections to Telegram servers.
@@ -222,7 +241,7 @@ export class NetworkManager {
222
241
  _: 'initConnection',
223
242
  deviceModel,
224
243
  systemVersion: '1.0',
225
- appVersion: '0.2.2',
244
+ appVersion: '0.3.0',
226
245
  systemLangCode: 'en',
227
246
  langPack: '',
228
247
  langCode: 'en',
@@ -408,6 +427,7 @@ export class NetworkManager {
408
427
  }
409
428
  const floodSleepThreshold = params?.floodSleepThreshold ?? this.params.floodSleepThreshold;
410
429
  const maxRetryCount = params?.maxRetryCount ?? this.params.maxRetryCount;
430
+ const throw503 = params?.throw503 ?? false;
411
431
  // do not send requests that are in flood wait
412
432
  if (this._floodWaitedRequests.has(message._)) {
413
433
  const delta = this._floodWaitedRequests.get(message._) - Date.now();
@@ -440,7 +460,7 @@ export class NetworkManager {
440
460
  let multi = manager[kind];
441
461
  for (let i = 0; i < maxRetryCount; i++) {
442
462
  try {
443
- const res = await multi.sendRpc(message, stack, params?.timeout, params?.abortSignal);
463
+ const res = await multi.sendRpc(message, stack, params?.timeout, params?.abortSignal, params?.chainId);
444
464
  if (kind === 'main') {
445
465
  this._lastUpdateTime = Date.now();
446
466
  }
@@ -454,7 +474,10 @@ export class NetworkManager {
454
474
  throw e;
455
475
  }
456
476
  if (!(e.code in CLIENT_ERRORS)) {
457
- this._log.warn('Telegram is having internal issues: %d %s, retrying', e.code, e.message);
477
+ if (throw503 && e.code === -503) {
478
+ throw new MtTimeoutError();
479
+ }
480
+ this._log.warn('Telegram is having internal issues: %d:%s (%s), retrying', e.code, e.text, e.message);
458
481
  if (e.text === 'WORKER_BUSY_TOO_LONG_RETRY') {
459
482
  // according to tdlib, "it is dangerous to resend query without timeout, so use 1"
460
483
  await sleep(1000);
@@ -530,10 +553,7 @@ export class NetworkManager {
530
553
  }
531
554
  destroy() {
532
555
  for (const dc of this._dcConnections.values()) {
533
- dc.main.destroy();
534
- dc.upload.destroy();
535
- dc.download.destroy();
536
- dc.downloadSmall.destroy();
556
+ dc.destroy();
537
557
  }
538
558
  if (this._keepAliveInterval)
539
559
  clearInterval(this._keepAliveInterval);