|
|
@ -12,7 +12,6 @@ import { longToBytes } from '../crypto/crypto_utils'; |
|
|
|
import MTTransport from './transports/transport'; |
|
|
|
import MTTransport from './transports/transport'; |
|
|
|
import { convertToUint8Array, bufferConcat, bytesCmp, bytesToHex } from '../../helpers/bytes'; |
|
|
|
import { convertToUint8Array, bufferConcat, bytesCmp, bytesToHex } from '../../helpers/bytes'; |
|
|
|
import { nextRandomInt } from '../../helpers/random'; |
|
|
|
import { nextRandomInt } from '../../helpers/random'; |
|
|
|
import { CancellablePromise } from '../../helpers/cancellablePromise'; |
|
|
|
|
|
|
|
import App from '../../config/app'; |
|
|
|
import App from '../../config/app'; |
|
|
|
import DEBUG from '../../config/debug'; |
|
|
|
import DEBUG from '../../config/debug'; |
|
|
|
import Modes from '../../config/modes'; |
|
|
|
import Modes from '../../config/modes'; |
|
|
@ -108,11 +107,13 @@ export default class MTPNetworker { |
|
|
|
|
|
|
|
|
|
|
|
public isOnline = false; |
|
|
|
public isOnline = false; |
|
|
|
private lastResponseTime = 0; |
|
|
|
private lastResponseTime = 0; |
|
|
|
private disconnectDelay: number; |
|
|
|
|
|
|
|
private pingPromise: CancellablePromise<any>; |
|
|
|
private schedulePromise: Promise<any>; |
|
|
|
|
|
|
|
//private disconnectDelay: number;
|
|
|
|
|
|
|
|
//private pingPromise: CancellablePromise<any>;
|
|
|
|
//public onConnectionStatusChange: (online: boolean) => void;
|
|
|
|
//public onConnectionStatusChange: (online: boolean) => void;
|
|
|
|
|
|
|
|
|
|
|
|
private debugRequests: Array<{before: Uint8Array, after: Uint8Array}> = []; |
|
|
|
//private debugRequests: Array<{before: Uint8Array, after: Uint8Array}> = [];
|
|
|
|
|
|
|
|
|
|
|
|
constructor(public dcId: number, private authKey: number[], private authKeyId: Uint8Array, |
|
|
|
constructor(public dcId: number, private authKey: number[], private authKeyId: Uint8Array, |
|
|
|
serverSalt: number[], private transport: MTTransport, options: InvokeApiOptions = {}) { |
|
|
|
serverSalt: number[], private transport: MTTransport, options: InvokeApiOptions = {}) { |
|
|
@ -641,9 +642,9 @@ export default class MTPNetworker { |
|
|
|
this.log.error('timeout', message); |
|
|
|
this.log.error('timeout', message); |
|
|
|
this.setConnectionStatus(false); |
|
|
|
this.setConnectionStatus(false); |
|
|
|
|
|
|
|
|
|
|
|
this.getEncryptedOutput(message).then(bytes => { |
|
|
|
/* this.getEncryptedOutput(message).then(bytes => { |
|
|
|
this.log.error('timeout encrypted', bytes); |
|
|
|
this.log.error('timeout encrypted', bytes); |
|
|
|
}); |
|
|
|
}); */ |
|
|
|
}, CONNECTION_TIMEOUT); |
|
|
|
}, CONNECTION_TIMEOUT); |
|
|
|
|
|
|
|
|
|
|
|
promise.finally(() => { |
|
|
|
promise.finally(() => { |
|
|
@ -683,7 +684,7 @@ export default class MTPNetworker { |
|
|
|
} */ |
|
|
|
} */ |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public pushResend(messageId: string, delay = 0) { |
|
|
|
public pushResend(messageId: string, delay = 100) { |
|
|
|
const value = delay ? Date.now() + delay : 0; |
|
|
|
const value = delay ? Date.now() + delay : 0; |
|
|
|
const sentMessage = this.sentMessages[messageId]; |
|
|
|
const sentMessage = this.sentMessages[messageId]; |
|
|
|
if(sentMessage.container) { |
|
|
|
if(sentMessage.container) { |
|
|
@ -784,8 +785,8 @@ export default class MTPNetworker { |
|
|
|
}; |
|
|
|
}; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
let message: MTPNetworker['sentMessages'][keyof MTPNetworker['sentMessages']]; |
|
|
|
let outMessage: MTPNetworker['sentMessages'][keyof MTPNetworker['sentMessages']]; |
|
|
|
const messages: typeof message[] = []; |
|
|
|
const messages: typeof outMessage[] = []; |
|
|
|
|
|
|
|
|
|
|
|
const currentTime = Date.now(); |
|
|
|
const currentTime = Date.now(); |
|
|
|
let messagesByteLen = 0; |
|
|
|
let messagesByteLen = 0; |
|
|
@ -796,8 +797,9 @@ export default class MTPNetworker { |
|
|
|
for(const messageId in this.pendingMessages) { |
|
|
|
for(const messageId in this.pendingMessages) { |
|
|
|
const value = this.pendingMessages[messageId]; |
|
|
|
const value = this.pendingMessages[messageId]; |
|
|
|
|
|
|
|
|
|
|
|
if(!value || value >= currentTime) { |
|
|
|
if(!value || value <= currentTime) { |
|
|
|
if(message = this.sentMessages[messageId]) { |
|
|
|
const message = this.sentMessages[messageId]; |
|
|
|
|
|
|
|
if(message) { |
|
|
|
/* if(message.fileUpload) { |
|
|
|
/* if(message.fileUpload) { |
|
|
|
this.log('performScheduledRequest message:', message, message.body.length, (message.body as Uint8Array).byteLength, (message.body as Uint8Array).buffer.byteLength); |
|
|
|
this.log('performScheduledRequest message:', message, message.body.length, (message.body as Uint8Array).byteLength, (message.body as Uint8Array).buffer.byteLength); |
|
|
|
} */ |
|
|
|
} */ |
|
|
@ -810,7 +812,7 @@ export default class MTPNetworker { |
|
|
|
if(!message.notContentRelated && |
|
|
|
if(!message.notContentRelated && |
|
|
|
messagesByteLen && |
|
|
|
messagesByteLen && |
|
|
|
messagesByteLen + messageByteLength > 655360) { // 640 Kb
|
|
|
|
messagesByteLen + messageByteLength > 655360) { // 640 Kb
|
|
|
|
this.log.warn('lengthOverflow', message); |
|
|
|
this.log.warn('lengthOverflow', message, messages); |
|
|
|
lengthOverflow = true; |
|
|
|
lengthOverflow = true; |
|
|
|
continue; // maybe break here
|
|
|
|
continue; // maybe break here
|
|
|
|
} |
|
|
|
} |
|
|
@ -822,6 +824,8 @@ export default class MTPNetworker { |
|
|
|
} else if(message.longPoll) { |
|
|
|
} else if(message.longPoll) { |
|
|
|
hasHttpWait = true; |
|
|
|
hasHttpWait = true; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
outMessage = message; |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
// this.log(message, messageId)
|
|
|
|
// this.log(message, messageId)
|
|
|
|
} |
|
|
|
} |
|
|
@ -855,9 +859,43 @@ export default class MTPNetworker { |
|
|
|
return; |
|
|
|
return; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
const noResponseMsgs: Array<string> = []; |
|
|
|
/// #if MTPROTO_HTTP_UPLOAD || MTPROTO_HTTP
|
|
|
|
|
|
|
|
const noResponseMsgs: Array<string> = messages.filter(message => message.noResponse).map(message => message.msg_id); |
|
|
|
|
|
|
|
/// #endif
|
|
|
|
|
|
|
|
|
|
|
|
if(messages.length > 1) { |
|
|
|
if(messages.length > 1) { |
|
|
|
|
|
|
|
const container = this.generateContainerMessage(messagesByteLen, messages); |
|
|
|
|
|
|
|
outMessage = container.messageWithBody; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
this.sentMessages[outMessage.msg_id] = container.message; |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
this.sentMessages[outMessage.msg_id] = outMessage; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
this.pendingAcks = []; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const promise = this.sendEncryptedRequest(outMessage); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// #if MTPROTO_HTTP_UPLOAD
|
|
|
|
|
|
|
|
if(!(this.transport instanceof HTTP)) { |
|
|
|
|
|
|
|
//if(noResponseMsgs.length) this.log.error('noResponseMsgs length!', noResponseMsgs);
|
|
|
|
|
|
|
|
this.cleanupSent(); // ! WARNING
|
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
this.handleSentEncryptedRequestHTTP(promise, outMessage, noResponseMsgs); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
/// #elif !MTPROTO_HTTP
|
|
|
|
|
|
|
|
this.cleanupSent(); // ! WARNING
|
|
|
|
|
|
|
|
/// #else
|
|
|
|
|
|
|
|
this.handleSentEncryptedRequestHTTP(promise, outMessage, noResponseMsgs); |
|
|
|
|
|
|
|
//}
|
|
|
|
|
|
|
|
/// #endif
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if(lengthOverflow) { |
|
|
|
|
|
|
|
this.scheduleRequest(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private generateContainerMessage(messagesByteLen: number, messages: MTMessage[]) { |
|
|
|
const container = new TLSerialization({ |
|
|
|
const container = new TLSerialization({ |
|
|
|
mtproto: true, |
|
|
|
mtproto: true, |
|
|
|
startMaxLength: messagesByteLen + 64 |
|
|
|
startMaxLength: messagesByteLen + 64 |
|
|
@ -868,65 +906,29 @@ export default class MTPNetworker { |
|
|
|
|
|
|
|
|
|
|
|
const innerMessages: string[] = []; |
|
|
|
const innerMessages: string[] = []; |
|
|
|
messages.forEach((message, i) => { |
|
|
|
messages.forEach((message, i) => { |
|
|
|
container.storeLong(message.msg_id, 'CONTAINER[' + i + '][msg_id]'); |
|
|
|
|
|
|
|
innerMessages.push(message.msg_id); |
|
|
|
innerMessages.push(message.msg_id); |
|
|
|
|
|
|
|
container.storeLong(message.msg_id, 'CONTAINER[' + i + '][msg_id]'); |
|
|
|
container.storeInt(message.seq_no, 'CONTAINER[' + i + '][seq_no]'); |
|
|
|
container.storeInt(message.seq_no, 'CONTAINER[' + i + '][seq_no]'); |
|
|
|
container.storeInt(message.body.length, 'CONTAINER[' + i + '][bytes]'); |
|
|
|
container.storeInt(message.body.length, 'CONTAINER[' + i + '][bytes]'); |
|
|
|
container.storeRawBytes(message.body, 'CONTAINER[' + i + '][body]'); |
|
|
|
container.storeRawBytes(message.body, 'CONTAINER[' + i + '][body]'); |
|
|
|
if(message.noResponse) { |
|
|
|
|
|
|
|
noResponseMsgs.push(message.msg_id); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}); |
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
const containerSentMessage: MTMessage = { |
|
|
|
const message: MTMessage = { |
|
|
|
msg_id: timeManager.generateId(), |
|
|
|
msg_id: timeManager.generateId(), |
|
|
|
seq_no: this.generateSeqNo(true), |
|
|
|
seq_no: this.generateSeqNo(true), |
|
|
|
container: true, |
|
|
|
container: true, |
|
|
|
inner: innerMessages |
|
|
|
inner: innerMessages |
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
message = Object.assign({ |
|
|
|
if(Modes.debug/* || true */) { |
|
|
|
body: container.getBytes(true) |
|
|
|
this.log.warn('Container', innerMessages, message.msg_id, message.seq_no); |
|
|
|
}, containerSentMessage); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
this.sentMessages[message.msg_id] = containerSentMessage; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if(Modes.debug) { |
|
|
|
|
|
|
|
this.log('Container', innerMessages, message.msg_id, message.seq_no); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
if(message.noResponse) { |
|
|
|
|
|
|
|
noResponseMsgs.push(message.msg_id); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
this.sentMessages[message.msg_id] = message; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
this.pendingAcks = []; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const promise = this.sendEncryptedRequest(message); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// #if MTPROTO_HTTP_UPLOAD
|
|
|
|
|
|
|
|
if(!(this.transport instanceof HTTP)) { |
|
|
|
|
|
|
|
//if(noResponseMsgs.length) this.log.error('noResponseMsgs length!', noResponseMsgs);
|
|
|
|
|
|
|
|
this.cleanupSent(); // ! WARNING
|
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
this.handleSentEncryptedRequestHTTP(promise, message, noResponseMsgs); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
/// #elif !MTPROTO_HTTP
|
|
|
|
|
|
|
|
//if(!(this.transport instanceof HTTP)) {
|
|
|
|
|
|
|
|
//if(noResponseMsgs.length) this.log.error('noResponseMsgs length!', noResponseMsgs);
|
|
|
|
|
|
|
|
this.cleanupSent(); // ! WARNING
|
|
|
|
|
|
|
|
//} else {
|
|
|
|
|
|
|
|
/// #else
|
|
|
|
|
|
|
|
this.handleSentEncryptedRequestHTTP(promise, message, noResponseMsgs); |
|
|
|
|
|
|
|
//}
|
|
|
|
|
|
|
|
/// #endif
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if(lengthOverflow) { |
|
|
|
return { |
|
|
|
this.scheduleRequest(); |
|
|
|
message, |
|
|
|
} |
|
|
|
messageWithBody: Object.assign({body: container.getBytes(true)}, message), |
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public async getEncryptedMessage(dataWithPadding: ArrayBuffer) { |
|
|
|
public async getEncryptedMessage(dataWithPadding: ArrayBuffer) { |
|
|
|
const msgKey = await this.getMsgKey(dataWithPadding, true); |
|
|
|
const msgKey = await this.getMsgKey(dataWithPadding, true); |
|
|
@ -1045,6 +1047,8 @@ export default class MTPNetworker { |
|
|
|
|
|
|
|
|
|
|
|
public sendEncryptedRequest(message: MTMessage) { |
|
|
|
public sendEncryptedRequest(message: MTMessage) { |
|
|
|
return this.getEncryptedOutput(message).then(requestData => { |
|
|
|
return this.getEncryptedOutput(message).then(requestData => { |
|
|
|
|
|
|
|
//this.log('sendEncryptedRequest: launching message into space:', message);
|
|
|
|
|
|
|
|
|
|
|
|
const promise: Promise<Uint8Array> = this.transport.send(requestData) as any; |
|
|
|
const promise: Promise<Uint8Array> = this.transport.send(requestData) as any; |
|
|
|
/// #if !MTPROTO_HTTP && !MTPROTO_HTTP_UPLOAD
|
|
|
|
/// #if !MTPROTO_HTTP && !MTPROTO_HTTP_UPLOAD
|
|
|
|
return promise; |
|
|
|
return promise; |
|
|
@ -1206,27 +1210,20 @@ export default class MTPNetworker { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// ! таймаут очень сильно тормозит скорость работы сокета (даже нулевой)
|
|
|
|
// ! таймаут очень сильно тормозит скорость работы сокета (даже нулевой)
|
|
|
|
public scheduleRequest(delay = 0) { |
|
|
|
public scheduleRequest(delay?: number) { |
|
|
|
/// #if !MTPROTO_HTTP && !MTPROTO_HTTP_UPLOAD
|
|
|
|
/// #if MTPROTO_HTTP || MTPROTO_HTTP_UPLOAD
|
|
|
|
/* clearTimeout(this.nextReqTimeout); |
|
|
|
if(!(this.transport instanceof HTTP)) { |
|
|
|
this.nextReqTimeout = self.setTimeout(this.performScheduledRequest.bind(this), delay || 0); |
|
|
|
this.performScheduledRequest(); |
|
|
|
return; */ |
|
|
|
return; |
|
|
|
return this.performScheduledRequest(); |
|
|
|
} else if(this.offline) { |
|
|
|
/// #else
|
|
|
|
|
|
|
|
if(!(this.transport instanceof HTTP)) return this.performScheduledRequest(); |
|
|
|
|
|
|
|
if(this.offline/* && this.transport instanceof HTTP */) { |
|
|
|
|
|
|
|
this.checkConnection('forced schedule'); |
|
|
|
this.checkConnection('forced schedule'); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/// #endif
|
|
|
|
|
|
|
|
|
|
|
|
/* if(delay && !(this.transport instanceof HTTP)) { |
|
|
|
const nextReq = Date.now() + (delay || 0); |
|
|
|
delay = 0; |
|
|
|
if(this.nextReq && (delay === undefined || this.nextReq <= nextReq)) { |
|
|
|
} */ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const nextReq = Date.now() + delay; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if(delay && this.nextReq && this.nextReq <= nextReq) { |
|
|
|
|
|
|
|
//this.log('scheduleRequest: nextReq', this.nextReq, nextReq);
|
|
|
|
//this.log('scheduleRequest: nextReq', this.nextReq, nextReq);
|
|
|
|
return false; |
|
|
|
return; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
//this.log('scheduleRequest: delay', delay);
|
|
|
|
//this.log('scheduleRequest: delay', delay);
|
|
|
@ -1235,33 +1232,43 @@ export default class MTPNetworker { |
|
|
|
return; |
|
|
|
return; |
|
|
|
} */ |
|
|
|
} */ |
|
|
|
|
|
|
|
|
|
|
|
const perf = performance.now(); |
|
|
|
//const perf = performance.now();
|
|
|
|
|
|
|
|
if(this.nextReqTimeout) { |
|
|
|
clearTimeout(this.nextReqTimeout); |
|
|
|
clearTimeout(this.nextReqTimeout); |
|
|
|
this.nextReqTimeout = self.setTimeout(() => { |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const cb = () => { |
|
|
|
//this.log('scheduleRequest: timeout delay was:', performance.now() - perf);
|
|
|
|
//this.log('scheduleRequest: timeout delay was:', performance.now() - perf);
|
|
|
|
|
|
|
|
|
|
|
|
this.nextReqTimeout = 0; |
|
|
|
this.nextReqTimeout = 0; |
|
|
|
|
|
|
|
this.nextReq = 0; |
|
|
|
|
|
|
|
|
|
|
|
/// #if MTPROTO_HTTP || MTPROTO_HTTP_UPLOAD || true
|
|
|
|
|
|
|
|
if(this.offline) { |
|
|
|
if(this.offline) { |
|
|
|
//this.log('Cancel scheduled');
|
|
|
|
//this.log('Cancel scheduled');
|
|
|
|
return false; |
|
|
|
return; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
this.nextReq = 0; |
|
|
|
|
|
|
|
/// #endif
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
this.performScheduledRequest(); |
|
|
|
this.performScheduledRequest(); |
|
|
|
}, delay); |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
this.nextReq = nextReq; |
|
|
|
this.nextReq = nextReq; |
|
|
|
/// #endif
|
|
|
|
|
|
|
|
|
|
|
|
if(delay) { |
|
|
|
|
|
|
|
this.nextReqTimeout = self.setTimeout(cb, delay); |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
cb(); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public ackMessage(msgId: string) { |
|
|
|
public ackMessage(msgId: string) { |
|
|
|
// this.log('ack message', msgID)
|
|
|
|
// this.log('ack message', msgID)
|
|
|
|
this.pendingAcks.push(msgId); |
|
|
|
this.pendingAcks.push(msgId); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// #if MTPROTO_HTTP || MTPROTO_HTTP_UPLOAD
|
|
|
|
this.scheduleRequest(30000); |
|
|
|
this.scheduleRequest(30000); |
|
|
|
|
|
|
|
/// #else
|
|
|
|
|
|
|
|
this.scheduleRequest(); |
|
|
|
|
|
|
|
/// #endif
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public reqResendMessage(msgId: string) { |
|
|
|
public reqResendMessage(msgId: string) { |
|
|
@ -1322,7 +1329,8 @@ export default class MTPNetworker { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* только для сокета, возможно это будет неправильно работать, но в тесте сработало правильно |
|
|
|
* * только для сокета |
|
|
|
|
|
|
|
* TODO: consider about containers resend |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
public resend() { |
|
|
|
public resend() { |
|
|
|
for(const id in this.sentMessages) { |
|
|
|
for(const id in this.sentMessages) { |
|
|
|