From df74c2b7e760f585ba93dc08c72c539f476dc282 Mon Sep 17 00:00:00 2001 From: morethanwords Date: Tue, 9 Jun 2020 12:43:42 +0300 Subject: [PATCH] Fix first requests with new transport Fix resend websocket messages after close --- src/lib/mtproto/authorizer.ts | 116 ++++++++++++------------ src/lib/mtproto/mtproto_config.ts | 4 +- src/lib/mtproto/mtprotoworker.ts | 2 +- src/lib/mtproto/networker.ts | 73 ++++++++++----- src/lib/mtproto/rsaKeysManager.ts | 4 +- src/lib/mtproto/transports/websocket.ts | 77 ++++++++++------ 6 files changed, 162 insertions(+), 114 deletions(-) diff --git a/src/lib/mtproto/authorizer.ts b/src/lib/mtproto/authorizer.ts index 75ccc148..48b44a5f 100644 --- a/src/lib/mtproto/authorizer.ts +++ b/src/lib/mtproto/authorizer.ts @@ -1,6 +1,6 @@ import { TLSerialization, TLDeserialization } from "./tl_utils"; import dcConfigurator from "./dcConfigurator"; -import { dT, bytesToHex, bytesCmp, bytesFromHex, bytesXor } from "../bin_utils"; +import { bytesToHex, bytesCmp, bytesFromHex, bytesXor } from "../bin_utils"; import rsaKeysManager from "./rsaKeysManager"; import timeManager from "./timeManager"; @@ -9,6 +9,8 @@ import { BigInteger } from "jsbn"; import CryptoWorker from "../crypto/cryptoworker"; +import { logger, LogLevels } from "../polyfill"; + /* let fNewNonce: any = bytesFromHex('8761970c24cb2329b5b2459752c502f3057cb7e8dbab200e526e8767fdc73b3c').reverse(); let fNonce: any = bytesFromHex('b597720d11faa5914ef485c529cde414').reverse(); let fResult: any = new Uint8Array(bytesFromHex('000000000000000001b473a0661b285e480000006324160514e4cd29c585f44e91a5fa110d7297b5c0c4134c84893db5715ecd56af5ed618082182053cc5de91cd00000015c4b51c02000000a5b7f709355fc30b216be86c022bb4c3')); @@ -59,7 +61,10 @@ export class Authorizer { [dcID: number]: Promise } = {}; + private log: ReturnType; + constructor() { + this.log = logger(`AUTHORIZER`/* , LogLevels.error | LogLevels.log */); } public mtpSendPlainRequest(dcID: number, requestArray: Uint8Array) { @@ -94,10 +99,10 @@ export class Authorizer { transport: transport }; - console.log(dT(), 'mtpSendPlainRequest: creating requestPromise'); + this.log('mtpSendPlainRequest: creating requestPromise'); return transport.send(resultArray).then(result => { - console.log(dT(), 'mtpSendPlainRequest: in good sector', result); + this.log('mtpSendPlainRequest: in good sector', result); if(!result || !result.byteLength) { return Promise.reject(baseError); @@ -109,17 +114,17 @@ export class Authorizer { let deserializer = new TLDeserialization(result, {mtproto: true}); let auth_key_id = deserializer.fetchLong('auth_key_id'); - if(auth_key_id != 0) console.error('auth_key_id != 0', auth_key_id); + if(auth_key_id != 0) this.log.error('auth_key_id != 0', auth_key_id); let msg_id = deserializer.fetchLong('msg_id'); - if(msg_id == 0) console.error('msg_id == 0', msg_id); + if(msg_id == 0) this.log.error('msg_id == 0', msg_id); let msg_len = deserializer.fetchInt('msg_len'); - if(!msg_len) console.error('no msg_len', msg_len); + if(!msg_len) this.log.error('no msg_len', msg_len); return deserializer; } catch(e) { - console.error('mtpSendPlainRequest: deserialization went bad', e); + this.log.error('mtpSendPlainRequest: deserialization went bad', e); let error = Object.assign(baseError, {originalError: e}); throw error; } @@ -142,15 +147,14 @@ export class Authorizer { // need rsaKeysManager.prepare().then(() => {}); - console.log(dT(), 'Send req_pq', auth.nonce.hex); + this.log('Send req_pq', auth.nonce.hex); try { var deserializer = await this.mtpSendPlainRequest(auth.dcID, request.getBytes(true)); } catch(error) { - console.error(dT(), 'req_pq error', error.message); + this.log.error('req_pq error', error.message); throw error; } - var response = deserializer.fetchObject('ResPQ'); if(response._ != 'resPQ') { @@ -158,7 +162,7 @@ export class Authorizer { } if(!bytesCmp(auth.nonce, response.nonce)) { - console.log(auth.nonce, response.nonce); + this.log.error(auth.nonce, response.nonce); throw new Error('[MT] resPQ nonce mismatch'); } @@ -167,7 +171,7 @@ export class Authorizer { auth.pq = response.pq; auth.fingerprints = response.server_public_key_fingerprints; - console.log(dT(), 'Got ResPQ', bytesToHex(auth.serverNonce), bytesToHex(auth.pq), auth.fingerprints); + this.log('Got ResPQ', bytesToHex(auth.serverNonce), bytesToHex(auth.pq), auth.fingerprints); let publicKey = await rsaKeysManager.select(auth.fingerprints); if(!publicKey) { @@ -176,19 +180,19 @@ export class Authorizer { auth.publicKey = publicKey; - console.log(dT(), 'PQ factorization start', auth.pq); + this.log('PQ factorization start', auth.pq); try { var pAndQ = await CryptoWorker.factorize(auth.pq); } catch(error) { - console.error('worker error factorize', error); + this.log.error('worker error factorize', error); throw error; } auth.p = pAndQ[0]; auth.q = pAndQ[1]; - console.log(dT(), 'PQ factorization done', pAndQ); + this.log('PQ factorization done', pAndQ); /* let p = new Uint32Array(new Uint8Array(auth.p).buffer)[0]; let q = new Uint32Array(new Uint8Array(auth.q).buffer)[0]; console.log(dT(), 'PQ factorization done', pAndQ, p.toString(16), q.toString(16)); */ @@ -253,18 +257,18 @@ export class Authorizer { let requestBytes = request.getBytes(true); - console.log(dT(), 'Send req_DH_params', req_DH_params/* , requestBytes.hex */); + this.log('Send req_DH_params', req_DH_params/* , requestBytes.hex */); try { var deserializer = await this.mtpSendPlainRequest(auth.dcID, requestBytes); } catch(error) { - console.log(dT(), 'Send req_DH_params FAIL!', error); + this.log('Send req_DH_params FAIL!', error); throw error; } var response = deserializer.fetchObject('Server_DH_Params', 'RESPONSE'); - console.log(dT(), 'Sent req_DH_params, response:', response); + this.log('Sent req_DH_params, response:', response); if(response._ != 'server_DH_params_fail' && response._ != 'server_DH_params_ok') { throw new Error('[MT] Server_DH_Params response invalid: ' + response._); @@ -292,7 +296,7 @@ export class Authorizer { try { await this.mtpDecryptServerDhDataAnswer(auth, response.encrypted_answer); } catch(e) { - console.error(dT(), 'mtpDecryptServerDhDataAnswer FAILED!', e); + this.log.error('mtpDecryptServerDhDataAnswer FAILED!', e); throw e; } @@ -342,7 +346,7 @@ export class Authorizer { throw new Error('[MT] server_DH_inner_data serverNonce mismatch'); } - console.log(dT(), 'Done decrypting answer'); + this.log('Done decrypting answer'); auth.g = response.g; auth.dhPrime = response.dh_prime; auth.gA = response.g_a; @@ -362,13 +366,13 @@ export class Authorizer { } public mtpVerifyDhParams(g: number, dhPrime: any, gA: any) { - console.log(dT(), 'Verifying DH params'); + this.log('Verifying DH params'); var dhPrimeHex = bytesToHex(dhPrime); if(g != 3 || dhPrimeHex !== 'c71caeb9c6b1c9048e6c522f70f13f73980d40238e3e21c14934d037563d930f48198a0aa7c14058229493d22530f4dbfa336f6e0ac925139543aed44cce7c3720fd51f69458705ac68cd4fe6b6b13abdc9746512969328454f18faf8c595f642477fe96bb2a941d5bcd1d4ac8cc49880708fa9b378e3c4f3a9060bee67cf9a4a4a695811051907e162753b56b0f6b410dba74d8a84b2a14b3144e0ef1284754fd17ed950d5965b4b9dd46582db1178d169c6bc465b0d6ff9ca3928fef5b9ae4e418fc15e83ebea0f87fa9ff5eed70050ded2849f47bf959d956850ce929851f0d8115f635b105ee2e4e15d04b2454bf6f4fadf034b10403119cd8e3b92fcc5b') { // The verified value is from https://core.telegram.org/mtproto/security_guidelines throw new Error('[MT] DH params are not verified: unknown dhPrime'); } - console.log(dT(), 'dhPrime cmp OK'); + this.log('dhPrime cmp OK'); var gABigInt = new BigInteger(bytesToHex(gA), 16); var dhPrimeBigInt = new BigInteger(dhPrimeHex, 16); @@ -380,7 +384,7 @@ export class Authorizer { if(gABigInt.compareTo(dhPrimeBigInt.subtract(BigInteger.ONE)) >= 0) { throw new Error('[MT] DH params are not verified: gA >= dhPrime - 1'); } - console.log(dT(), '1 < gA < dhPrime-1 OK'); + this.log('1 < gA < dhPrime-1 OK'); var two = new BigInteger(/* null */''); @@ -393,7 +397,7 @@ export class Authorizer { if(gABigInt.compareTo(dhPrimeBigInt.subtract(twoPow)) >= 0) { throw new Error('[MT] DH params are not verified: gA > dhPrime - 2^{2048-64}'); } - console.log(dT(), '2^{2048-64} < gA < dhPrime-2^{2048-64} OK'); + this.log('2^{2048-64} < gA < dhPrime-2^{2048-64} OK'); return true; } @@ -433,7 +437,7 @@ export class Authorizer { encrypted_data: encryptedData }); - console.log(dT(), 'Send set_client_DH_params'); + this.log('Send set_client_DH_params'); try { var deserializer = await this.mtpSendPlainRequest(auth.dcID, request.getBytes(true)); @@ -466,43 +470,43 @@ export class Authorizer { authKeyAux = authKeyHash.slice(0, 8), authKeyID = authKeyHash.slice(-8); - console.log(dT(), 'Got Set_client_DH_params_answer', response._, authKey); + this.log('Got Set_client_DH_params_answer', response._, authKey); switch(response._) { case 'dh_gen_ok': - var newNonceHash1 = (await CryptoWorker.sha1Hash(auth.newNonce.concat([1], authKeyAux))).slice(-16); - //var newNonceHash1 = sha1BytesSync(auth.newNonce.concat([1], authKeyAux)).slice(-16); - - if(!bytesCmp(newNonceHash1, response.new_nonce_hash1)) { - throw new Error('[MT] Set_client_DH_params_answer new_nonce_hash1 mismatch'); - } - - var serverSalt = bytesXor(auth.newNonce.slice(0, 8), auth.serverNonce.slice(0, 8)); - console.log('Auth successfull!', authKeyID, authKey, serverSalt); - - auth.authKeyID = authKeyID; - auth.authKey = authKey; - auth.serverSalt = serverSalt; - - return auth; - break; + var newNonceHash1 = (await CryptoWorker.sha1Hash(auth.newNonce.concat([1], authKeyAux))).slice(-16); + //var newNonceHash1 = sha1BytesSync(auth.newNonce.concat([1], authKeyAux)).slice(-16); + + if(!bytesCmp(newNonceHash1, response.new_nonce_hash1)) { + throw new Error('[MT] Set_client_DH_params_answer new_nonce_hash1 mismatch'); + } + + var serverSalt = bytesXor(auth.newNonce.slice(0, 8), auth.serverNonce.slice(0, 8)); + this.log('Auth successfull!', authKeyID, authKey, serverSalt); + + auth.authKeyID = authKeyID; + auth.authKey = authKey; + auth.serverSalt = serverSalt; + + return auth; + break; case 'dh_gen_retry': - //var newNonceHash2 = sha1BytesSync(auth.newNonce.concat([2], authKeyAux)).slice(-16); - var newNonceHash2 = (await CryptoWorker.sha1Hash(auth.newNonce.concat([2], authKeyAux))).slice(-16); - if(!bytesCmp(newNonceHash2, response.new_nonce_hash2)) { - throw new Error('[MT] Set_client_DH_params_answer new_nonce_hash2 mismatch'); - } - - return this.mtpSendSetClientDhParams(auth); + //var newNonceHash2 = sha1BytesSync(auth.newNonce.concat([2], authKeyAux)).slice(-16); + var newNonceHash2 = (await CryptoWorker.sha1Hash(auth.newNonce.concat([2], authKeyAux))).slice(-16); + if(!bytesCmp(newNonceHash2, response.new_nonce_hash2)) { + throw new Error('[MT] Set_client_DH_params_answer new_nonce_hash2 mismatch'); + } + + return this.mtpSendSetClientDhParams(auth); case 'dh_gen_fail': - //var newNonceHash3 = sha1BytesSync(auth.newNonce.concat([3], authKeyAux)).slice(-16); - var newNonceHash3 = (await CryptoWorker.sha1Hash(auth.newNonce.concat([3], authKeyAux))).slice(-16); - if(!bytesCmp(newNonceHash3, response.new_nonce_hash3)) { - throw new Error('[MT] Set_client_DH_params_answer new_nonce_hash3 mismatch'); - } - - throw new Error('[MT] Set_client_DH_params_answer fail'); + //var newNonceHash3 = sha1BytesSync(auth.newNonce.concat([3], authKeyAux)).slice(-16); + var newNonceHash3 = (await CryptoWorker.sha1Hash(auth.newNonce.concat([3], authKeyAux))).slice(-16); + if(!bytesCmp(newNonceHash3, response.new_nonce_hash3)) { + throw new Error('[MT] Set_client_DH_params_answer new_nonce_hash3 mismatch'); + } + + throw new Error('[MT] Set_client_DH_params_answer fail'); } } diff --git a/src/lib/mtproto/mtproto_config.ts b/src/lib/mtproto/mtproto_config.ts index ab274435..ee8c0197 100644 --- a/src/lib/mtproto/mtproto_config.ts +++ b/src/lib/mtproto/mtproto_config.ts @@ -9,7 +9,7 @@ export const App = { export const Modes = { test: location.search.indexOf('test=1') > 0/* || true */, debug: location.search.indexOf('debug=1') > 0, - http: location.search.indexOf('http=1') > 0, - ssl: location.search.indexOf('ssl=1') > 0 || location.protocol == 'https:' && location.search.indexOf('ssl=0') == -1, + http: false, //location.search.indexOf('http=1') > 0, + ssl: true, // location.search.indexOf('ssl=1') > 0 || location.protocol == 'https:' && location.search.indexOf('ssl=0') == -1, multipleConnections: true }; diff --git a/src/lib/mtproto/mtprotoworker.ts b/src/lib/mtproto/mtprotoworker.ts index 80a1d4f1..fd20ddd1 100644 --- a/src/lib/mtproto/mtprotoworker.ts +++ b/src/lib/mtproto/mtprotoworker.ts @@ -121,7 +121,7 @@ class ApiManagerProxy extends CryptoWorkerMethods { stopTime?: number, rawError?: any } = {}): Promise { - console.log('will invokeApi:', method, params, options); + //console.log('will invokeApi:', method, params, options); return this.performTaskWorker('invokeApi', method, params, options); } diff --git a/src/lib/mtproto/networker.ts b/src/lib/mtproto/networker.ts index 39afbf69..967ef43a 100644 --- a/src/lib/mtproto/networker.ts +++ b/src/lib/mtproto/networker.ts @@ -12,7 +12,7 @@ import NetworkerFactory from './networkerFactory'; import dcConfigurator from './dcConfigurator'; import Socket from './transports/websocket'; import HTTP from './transports/http'; -import { logger } from '../polyfill'; +import { logger, LogLevels } from '../polyfill'; import { Modes, App } from './mtproto_config'; import { InvokeApiOptions } from '../../types'; @@ -79,8 +79,6 @@ class MTPNetworker { private onOnlineCb = this.checkConnection.bind(this); - private debug = false; - private lastResendReq: { req_msg_id: string, resend_msg_ids: Array @@ -99,6 +97,11 @@ class MTPNetworker { this.log = logger('NET-' + dcID + (this.upload ? '-U' : '')); this.log('constructor'/* , this.authKey, this.authKeyID, this.serverSalt */); + /* // Test resend after bad_server_salt + if(this.dcID == 1 && this.upload) { + this.serverSalt[0] = 0; + } */ + this.updateSession(); // if(!NetworkerFactory.offlineInited) { @@ -268,7 +271,7 @@ class MTPNetworker { if(Modes.debug/* || true */) { this.log('Api call', method, message, params, options); } else { - //////this.log('Api call', method); + this.log('Api call', method, params, options); } return this.pushMessage(message, options); @@ -344,28 +347,28 @@ class MTPNetworker { } public pushResend(messageID: string, delay = 0) { - var value = delay ? Date.now() + delay : 0; - var sentMessage = this.sentMessages[messageID]; + const value = delay ? Date.now() + delay : 0; + const sentMessage = this.sentMessages[messageID]; if(sentMessage.container) { - for(var i = 0; i < sentMessage.inner.length; i++) { + for(let i = 0, length = sentMessage.inner.length; i < length; i++) { this.pendingMessages[sentMessage.inner[i]] = value; } } else { this.pendingMessages[messageID] = value; } - // this.log('Resend due', messageID, this.pendingMessages) + this.log('Resend due', messageID, this.pendingMessages); this.scheduleRequest(delay); } public async getMsgKey(dataWithPadding: ArrayBuffer, isOut: boolean) { - var authKey = this.authKeyUint8; - var x = isOut ? 0 : 8 - var msgKeyLargePlain = bufferConcat(authKey.subarray(88 + x, 88 + x + 32), dataWithPadding); + const authKey = this.authKeyUint8; + const x = isOut ? 0 : 8 + const msgKeyLargePlain = bufferConcat(authKey.subarray(88 + x, 88 + x + 32), dataWithPadding); - let msgKeyLarge = await CryptoWorker.sha256Hash(msgKeyLargePlain); - var msgKey = new Uint8Array(msgKeyLarge).subarray(8, 24); + const msgKeyLarge = await CryptoWorker.sha256Hash(msgKeyLargePlain); + const msgKey = new Uint8Array(msgKeyLarge).subarray(8, 24); return msgKey; }; @@ -513,7 +516,7 @@ class MTPNetworker { /* for(var i = 0; i < this.pendingResends.length; i++) { resendMsgIDs.push(this.pendingResends[i]); } */ - // this.log('resendReq messages', resendMsgIDs) + this.log('resendReq messages', resendMsgIDs); this.wrapMtpMessage({ _: 'msg_resend_req', msg_ids: resendMsgIDs @@ -729,7 +732,7 @@ class MTPNetworker { public sendEncryptedRequest(message: any, options: any = {}) { var self = this; - this.debug && this.log('Send encrypted', message, options, this.authKeyID); + this.log.debug('Send encrypted', message, options, this.authKeyID); // console.trace() var data = new TLSerialization({ startMaxLength: message.body.length + 2048 @@ -740,7 +743,7 @@ class MTPNetworker { data.storeLong(message.msg_id, 'message_id'); data.storeInt(message.seq_no, 'seq_no'); - + data.storeInt(message.body.length, 'message_data_length'); data.storeRawBytes(message.body, 'message_data'); @@ -756,7 +759,7 @@ class MTPNetworker { // this.log('auth_key_id', bytesToHex(self.authKeyID)) return this.getEncryptedMessage(dataWithPadding).then((encryptedResult) => { - this.debug && this.log('Got encrypted out message', encryptedResult); + this.log.debug('Got encrypted out message', encryptedResult); let request = new TLSerialization({ startMaxLength: encryptedResult.bytes.byteLength + 256 @@ -796,7 +799,7 @@ class MTPNetworker { } public parseResponse(responseBuffer: Uint8Array) { - this.debug && this.log('Start parsing response'/* , responseBuffer */); + this.log.debug('Start parsing response'/* , responseBuffer */); let self = this; let deserializer = new TLDeserialization(responseBuffer); @@ -1016,6 +1019,18 @@ class MTPNetworker { }; } + /** + * только для сокета, возможно это будет неправильно работать, но в тесте сработало правильно + */ + public resend() { + for(let id in this.sentMessages) { + const msg = this.sentMessages[id]; + if(msg.body) { + this.pushResend(id); + } + } + } + public processMessage(message: any, messageID: string, sessionID: Uint8Array | number[]) { var msgidInt = parseInt(messageID/* .toString(10) */.substr(0, -10), 10); if(msgidInt % 2) { @@ -1023,7 +1038,7 @@ class MTPNetworker { return; } - this.debug && this.log('process message', message, messageID, sessionID); + this.log.debug('process message', message, messageID, sessionID); switch(message._) { case 'msg_container': @@ -1040,10 +1055,20 @@ class MTPNetworker { this.log(message.bad_msg_id, message.bad_msg_seqno); throw new Error('[MT] Bad server salt for invalid message'); } - + this.applyServerSalt(message.new_server_salt); this.pushResend(message.bad_msg_id); this.ackMessage(messageID); + + /* // simulate disconnect + try { + this.log('networker state:', this); + // @ts-ignore + this.transport.ws.close(1000); + } catch(err) { + this.log.error('transport', this.transport, err); + } */ + break; case 'bad_msg_notification': @@ -1084,7 +1109,7 @@ class MTPNetworker { case 'new_session_created': this.ackMessage(messageID); - this.log('new_session_created in my head'); + this.log.debug('new_session_created', message); //this.updateSession(); this.processMessageAck(message.first_msg_id); @@ -1150,7 +1175,7 @@ class MTPNetworker { } else { if(deferred) { if(Modes.debug) { - this.debug && this.log('Rpc response', message.result); + this.log.debug('Rpc response', message.result); } else { var dRes = message.result._; if(!dRes) { @@ -1160,7 +1185,7 @@ class MTPNetworker { dRes = message.result; } } - this.debug && this.log('Rpc response', dRes, sentMessage); + this.log.debug('Rpc response', dRes, sentMessage); } sentMessage.deferred.resolve(message.result); @@ -1179,7 +1204,7 @@ class MTPNetworker { default: this.ackMessage(messageID); - this.debug && this.log('Update', message); + this.log.debug('Update', message); if(NetworkerFactory.updatesProcessor !== null) { NetworkerFactory.updatesProcessor(message, true); diff --git a/src/lib/mtproto/rsaKeysManager.ts b/src/lib/mtproto/rsaKeysManager.ts index 5077273d..ff80f13a 100644 --- a/src/lib/mtproto/rsaKeysManager.ts +++ b/src/lib/mtproto/rsaKeysManager.ts @@ -113,7 +113,7 @@ export class RSAKeysManager { })).then(() => { this.prepared = true; - console.log('[MT] Prepared keys'); + //console.log('[MT] Prepared keys'); this.preparePromise = null; }); } @@ -130,7 +130,7 @@ export class RSAKeysManager { fingerprintHex = new Array(16 - fingerprintHex.length).fill('0').join('') + fingerprintHex; } - console.log(fingerprintHex, this.publicKeysParsed); + //console.log(fingerprintHex, this.publicKeysParsed); if(foundKey = this.publicKeysParsed[fingerprintHex]) { return Object.assign({ fingerprint: fingerprints[i] diff --git a/src/lib/mtproto/transports/websocket.ts b/src/lib/mtproto/transports/websocket.ts index 82fd1fca..7010b421 100644 --- a/src/lib/mtproto/transports/websocket.ts +++ b/src/lib/mtproto/transports/websocket.ts @@ -5,7 +5,7 @@ import {CTR} from '@cryptography/aes'; //import abridgetPacketCodec from './abridged'; import intermediatePacketCodec from './intermediate'; import {MTPNetworker} from '../networker'; -import { logger } from '../../polyfill'; +import { logger, LogLevels } from '../../polyfill'; import { bytesFromWordss } from '../../bin_utils'; import { Codec } from './codec'; @@ -108,9 +108,14 @@ export class Obfuscation { } export default class Socket extends MTTransport { - ws: WebSocket | undefined; - - pending: Array<{resolve?: any, reject?: any, body?: Uint8Array}> = []; + ws: WebSocket; + + pending: Array> = []; connected = false; @@ -122,14 +127,12 @@ export default class Socket extends MTTransport { log: ReturnType; - debug = false; - codec = intermediatePacketCodec; constructor(dcID: number, url: string) { super(dcID, url); - this.log = logger(`WS-${dcID}`); + this.log = logger(`WS-${dcID}`, LogLevels.log | LogLevels.error); this.log('constructor'); @@ -142,7 +145,7 @@ export default class Socket extends MTTransport { this.ws.removeEventListener('close', this.handleClose); this.ws.removeEventListener('message', this.handleMessage); this.ws.close(1000); - } + } this.ws = new WebSocket(this.url, 'binary'); this.ws.binaryType = 'arraybuffer'; @@ -161,20 +164,34 @@ export default class Socket extends MTTransport { }; handleClose = (event: CloseEvent) => { - this.log('closed', event); + this.log('closed', event, this.pending); this.connected = false; - this.pending.length = 0; - if(this.networker) { + //this.pending.length = 0; + /* if(this.networker) { + this.networker.resend(); this.networker.cleanupSent(); - } + } */ this.log('trying to reconnect...'); this.connect(); + + for(let pending of this.pending) { + if(pending.bodySent) { + pending.bodySent = false; + } + } + + if(this.networker) { + this.ws.addEventListener('open', () => { + this.networker.resend(); + this.networker.cleanupSent(); + }, {once: true}); + } }; handleMessage = (event: MessageEvent) => { - this.debug && this.log('<-', 'handleMessage', event); + this.log.debug('<-', 'handleMessage', event); let data = this.obfuscation.decode(new Uint8Array(event.data)); data = this.codec.readPacket(data); @@ -182,9 +199,9 @@ export default class Socket extends MTTransport { if(this.networker) { // authenticated! //this.pending = this.pending.filter(p => p.body); // clear pending - this.debug && this.log('redirecting to networker'); + this.log.debug('redirecting to networker'); return this.networker.parseResponse(data).then(response => { - this.debug && this.log('redirecting to networker response:', response); + this.log.debug('redirecting to networker response:', response); this.networker.processMessage(response.response, response.messageID, response.sessionID); }); } @@ -192,14 +209,14 @@ export default class Socket extends MTTransport { //console.log('got hex:', data.hex); let pending = this.pending.shift(); if(!pending) { - return this.log('no pending for res:', data.hex); + return this.log.debug('no pending for res:', data.hex); } pending.resolve(data); }; send = (body: Uint8Array) => { - this.debug && this.log('-> body length to pending:', body.length); + this.log.debug('-> body length to pending:', body.length); if(this.networker) { this.pending.push({body}); @@ -221,26 +238,28 @@ export default class Socket extends MTTransport { return; } - let length = this.pending.length; + //this.log.error('Pending length:', this.pending.length); + const length = this.pending.length; for(let i = length - 1; i >= 0; --i) { - let pending = this.pending[i]; - let {body} = pending; - if(body) { - let toEncode = this.codec.encodePacket(body); - - //console.log('send before obf:', /* body.hex, nonce.hex, */ toEncode.hex); - let enc = this.obfuscation.encode(toEncode); - //console.log('send after obf:', enc.hex); + const pending = this.pending[i]; + const {body, bodySent} = pending; + if(body && !bodySent) { + const toEncode = this.codec.encodePacket(body); - this.debug && this.log('-> body length to send:', enc.length); + //this.log('send before obf:', /* body.hex, nonce.hex, */ toEncode.hex); + const enc = this.obfuscation.encode(toEncode); + //this.log('send after obf:', enc.hex); + this.log.debug('-> body length to send:', enc.length); this.ws.send(enc); - + if(!pending.resolve) { // remove if no response needed this.pending.splice(i, 1); + } else { + pending.bodySent = true; } - delete pending.body; + //delete pending.body; } } }