diff --git a/src/config/modes.ts b/src/config/modes.ts index 3b975964..9c871268 100644 --- a/src/config/modes.ts +++ b/src/config/modes.ts @@ -18,7 +18,7 @@ const Modes = { ssl: true, // location.search.indexOf('ssl=1') > 0 || location.protocol === 'https:' && location.search.indexOf('ssl=0') === -1, multipleConnections: true, asServiceWorker: false, - transport: 'https' as TransportType + transport: 'websocket' as TransportType }; /// #if MTPROTO_HAS_HTTP diff --git a/src/lib/mtproto/apiManager.ts b/src/lib/mtproto/apiManager.ts index f11ae3c6..a1ed18a2 100644 --- a/src/lib/mtproto/apiManager.ts +++ b/src/lib/mtproto/apiManager.ts @@ -36,6 +36,10 @@ import Modes from '../../config/modes'; import rootScope from '../rootScope'; /// #endif +/// #if MTPROTO_AUTO +import transportController from './transports/controller'; +/// #endif + /* var networker = apiManager.cachedNetworkers.websocket.upload[2]; networker.wrapMtpMessage({ _: 'msgs_state_req', @@ -106,6 +110,12 @@ export class ApiManager { this.afterMessageTempIds = {}; this.transportType = Modes.transport; + + /// #if MTPROTO_AUTO + transportController.addEventListener('transport', (transportType) => { + this.changeTransportType(transportType); + }); + /// #endif } //private lol = false; @@ -168,6 +178,12 @@ export class ApiManager { public changeTransportType(transportType: TransportType) { const oldTransportType = this.transportType; + if(oldTransportType === transportType) { + return; + } + + this.log('changing transport from', oldTransportType, 'to', transportType); + const oldObject = this.cachedNetworkers[oldTransportType]; const newObject = this.cachedNetworkers[transportType]; this.cachedNetworkers[transportType] = oldObject; @@ -274,7 +290,7 @@ export class ApiManager { //const connectionType: ConnectionType = 'client'; const transportType = this.getTransportType(connectionType); - if(!this.cachedNetworkers.hasOwnProperty(transportType)) { + if(!this.cachedNetworkers[transportType]) { this.cachedNetworkers[transportType] = { client: {}, download: {}, @@ -312,7 +328,7 @@ export class ApiManager { const ak: DcAuthKey = `dc${dcId}_auth_key` as any; const ss: DcServerSalt = `dc${dcId}_server_salt` as any; - const transport = this.chooseServer(dcId, connectionType, transportType); + let transport = this.chooseServer(dcId, connectionType, transportType); return this.gettingNetworkers[getKey] = Promise.all([ak, ss].map(key => sessionStorage.get(key))) .then(async([authKeyHex, serverSaltHex]) => { let networker: MTPNetworker; @@ -325,19 +341,17 @@ export class ApiManager { const authKeyId = (await CryptoWorker.invokeCrypto('sha1-hash', authKey)).slice(-8); const serverSalt = bytesFromHex(serverSaltHex); - networker = networkerFactory.getNetworker(dcId, authKey, authKeyId, serverSalt, transport, options); + networker = networkerFactory.getNetworker(dcId, authKey, authKeyId, serverSalt, options); } else { try { // if no saved state const auth = await authorizer.auth(dcId); - const storeObj = { + sessionStorage.set({ [ak]: bytesToHex(auth.authKey), [ss]: bytesToHex(auth.serverSalt) - }; - - sessionStorage.set(storeObj); + }); - networker = networkerFactory.getNetworker(dcId, auth.authKey, auth.authKeyId, auth.serverSalt, transport, options); + networker = networkerFactory.getNetworker(dcId, auth.authKey, auth.authKeyId, auth.serverSalt, options); } catch(error) { this.log('Get networker error', error, (error as Error).stack); delete this.gettingNetworkers[getKey]; @@ -345,6 +359,16 @@ export class ApiManager { } } + // ! cannot get it before this promise because simultaneous changeTransport will change nothing + const newTransportType = this.getTransportType(connectionType); + if(newTransportType !== transportType) { + transport.destroy(); + DcConfigurator.removeTransport(dcConfigurator.chosenServers, transport); + transport = this.chooseServer(dcId, connectionType, newTransportType); + } + + networker.changeTransport(transport); + /* networker.onConnectionStatusChange = (online) => { console.log('status:', online); }; */ @@ -377,7 +401,6 @@ export class ApiManager { networker.destroy(); networkerFactory.removeNetworker(networker); DcConfigurator.removeTransport(this.cachedNetworkers, networker); - DcConfigurator.removeTransport(dcConfigurator.chosenServers, networker.transport); }; networker.setDrainTimeout(); diff --git a/src/lib/mtproto/authorizer.ts b/src/lib/mtproto/authorizer.ts index 7d28e7bd..320cb8a1 100644 --- a/src/lib/mtproto/authorizer.ts +++ b/src/lib/mtproto/authorizer.ts @@ -10,7 +10,7 @@ */ import { TLSerialization, TLDeserialization } from "./tl_utils"; -import dcConfigurator from "./dcConfigurator"; +import dcConfigurator, { TransportType } from "./dcConfigurator"; import rsaKeysManager from "./rsaKeysManager"; import timeManager from "./timeManager"; @@ -24,6 +24,10 @@ import { addPadding } from "./bin_utils"; import { Awaited, DcId } from "../../types"; import { ApiError } from "./apiManager"; +/// #if MTPROTO_AUTO +import transportController from "./transports/controller"; +/// #endif + /* let fNewNonce: any = bytesFromHex('8761970c24cb2329b5b2459752c502f3057cb7e8dbab200e526e8767fdc73b3c').reverse(); let fNonce: any = bytesFromHex('b597720d11faa5914ef485c529cde414').reverse(); let fResult: any = new Uint8Array(bytesFromHex('000000000000000001b473a0661b285e480000006324160514e4cd29c585f44e91a5fa110d7297b5c0c4134c84893db5715ecd56af5ed618082182053cc5de91cd00000015c4b51c02000000a5b7f709355fc30b216be86c022bb4c3')); @@ -103,6 +107,12 @@ export class Authorizer { }; private log: ReturnType; + + private transportType: TransportType; + + /// #if MTPROTO_AUTO + private getTransportTypePromise: Promise; + /// #endif constructor() { this.cached = {}; @@ -122,7 +132,7 @@ export class Authorizer { resultArray.set(headerArray); resultArray.set(requestArray, headerArray.length); - const transport = dcConfigurator.chooseServer(dcId); + const transport = dcConfigurator.chooseServer(dcId, 'client', this.transportType); const baseError = { code: 406, type: 'NETWORK_BAD_RESPONSE' @@ -570,18 +580,27 @@ export class Authorizer { } } } + + /// #if MTPROTO_AUTO + private getTransportType() { + if(this.getTransportTypePromise) return this.getTransportTypePromise; + return this.getTransportTypePromise = transportController.pingTransports().then(({websocket}) => { + this.transportType = websocket ? 'websocket' : 'https'; + }); + } + /// #endif public auth(dcId: DcId) { let promise = this.cached[dcId]; if(promise) { return promise; } - - if(!dcConfigurator.chooseServer(dcId)) { - throw new Error('[MT] No server found for dc ' + dcId); - } promise = new Promise(async(resolve, reject) => { + /// #if MTPROTO_AUTO + await this.getTransportType(); + /// #endif + let error: ApiError; let _try = 1; while(_try++ <= 3) { diff --git a/src/lib/mtproto/networker.ts b/src/lib/mtproto/networker.ts index f05717d5..ddceb3b0 100644 --- a/src/lib/mtproto/networker.ts +++ b/src/lib/mtproto/networker.ts @@ -36,6 +36,7 @@ import { bigInt2str, rightShift_, str2bigInt } from '../../vendor/leemon'; import { forEachReverse } from '../../helpers/array'; import { ConnectionStatus } from './connectionStatus'; import ctx from '../../environment/ctx'; +import dcConfigurator, { DcConfigurator } from './dcConfigurator'; //console.error('networker included!', new Error().stack); @@ -104,6 +105,7 @@ export default class MTPNetworker { /// #if MTPROTO_HAS_HTTP private longPollInterval: number; private longPollPending: number; + private checkConnectionRetryAt: number; private checkConnectionTimeout: number; private checkConnectionPeriod = 0; private sleepAfter: number; @@ -148,7 +150,6 @@ export default class MTPNetworker { private authKey: Uint8Array, private authKeyId: Uint8Array, serverSalt: Uint8Array, - transport: MTTransport, options: InvokeApiOptions = {} ) { this.authKeyUint8 = convertToUint8Array(this.authKey); @@ -178,8 +179,6 @@ export default class MTPNetworker { // rootScope.offlineConnecting = true */ // } - this.changeTransport(transport); - // * handle outcoming dead socket, server will close the connection // if((this.transport as TcpObfuscated).networker) { // this.disconnectDelay = /* (this.transport as TcpObfuscated).retryTimeout */75; @@ -366,9 +365,9 @@ export default class MTPNetworker { public changeTransport(transport?: MTTransport) { const oldTransport = this.transport; if(oldTransport) { - if((oldTransport as TcpObfuscated).destroy) { - (oldTransport as TcpObfuscated).destroy(); - } + oldTransport.destroy(); + + DcConfigurator.removeTransport(dcConfigurator.chosenServers, this.transport); if(this.nextReqTimeout) { clearTimeout(this.nextReqTimeout); @@ -394,22 +393,25 @@ export default class MTPNetworker { return; } + transport.networker = this; + /// #if MTPROTO_HAS_HTTP /// #if MTPROTO_HAS_WS if(transport instanceof HTTP) { /// #endif this.longPollInterval = ctx.setInterval(this.checkLongPoll, 10000); this.checkLongPoll(); + this.checkConnection('changed transport'); /// #if MTPROTO_HAS_WS } /// #endif /// #endif - transport.networker = this; - - if((transport as TcpObfuscated).connected) { + if(transport.connected && (transport as TcpObfuscated).connection) { this.setConnectionStatus(ConnectionStatus.Connected); } + + this.resend(); } public destroy() { @@ -419,12 +421,16 @@ export default class MTPNetworker { public forceReconnectTimeout() { if((this.transport as TcpObfuscated).reconnect) { (this.transport as TcpObfuscated).reconnect(); + } else { + this.resend(); } } public forceReconnect() { if((this.transport as TcpObfuscated).forceReconnect) { (this.transport as TcpObfuscated).forceReconnect(); + } else { + this.checkConnection('force reconnect'); } } @@ -603,6 +609,10 @@ export default class MTPNetworker { body: serializer.getBytes(true) }; + if(this.offline) { + this.setConnectionStatus(ConnectionStatus.Connecting); + } + this.sendEncryptedRequest(pingMessage).then(() => { this.toggleOffline(false); }, () => { @@ -612,91 +622,83 @@ export default class MTPNetworker { }); }; - private toggleOffline(enabled: boolean) { - // this.log('toggle ', enabled, this.dcId, this.iii) - if(this.offline !== undefined && this.offline === enabled) { - return false; - } - - this.offline = enabled; + private toggleOffline(offline: boolean) { + if(this.offline !== offline) { + this.offline = offline; - if(this.offline) { - clearTimeout(this.nextReqTimeout); - this.nextReqTimeout = 0; - this.nextReq = 0; - - if(this.checkConnectionPeriod < 1.5) { - this.checkConnectionPeriod = 0; - } - - this.checkConnectionTimeout = ctx.setTimeout(this.checkConnection, this.checkConnectionPeriod * 1000 | 0); - this.checkConnectionPeriod = Math.min(30, (1 + this.checkConnectionPeriod) * 1.5); - - /// #if !MTPROTO_WORKER - document.body.addEventListener('online', this.checkConnection, false); - document.body.addEventListener('focus', this.checkConnection, false); - /// #endif - } else { - this.checkLongPoll(); + if(offline) { + clearTimeout(this.nextReqTimeout); + this.nextReqTimeout = 0; + this.nextReq = 0; + + if(this.checkConnectionPeriod < 1.5) { + this.checkConnectionPeriod = 0; + } + + const delay = this.checkConnectionPeriod * 1000 | 0; + this.checkConnectionRetryAt = Date.now() + delay; + this.setConnectionStatus(ConnectionStatus.Closed, this.checkConnectionRetryAt); + this.checkConnectionTimeout = ctx.setTimeout(this.checkConnection, delay); + this.checkConnectionPeriod = Math.min(30, (1 + this.checkConnectionPeriod) * 1.5); + + /// #if !MTPROTO_WORKER + document.body.addEventListener('online', this.checkConnection, false); + document.body.addEventListener('focus', this.checkConnection, false); + /// #endif + } else { + this.setConnectionStatus(ConnectionStatus.Connected); + this.checkLongPoll(); - this.scheduleRequest(); - - /// #if !MTPROTO_WORKER - document.body.removeEventListener('online', this.checkConnection); - document.body.removeEventListener('focus', this.checkConnection); - /// #endif + this.scheduleRequest(); + + /// #if !MTPROTO_WORKER + document.body.removeEventListener('online', this.checkConnection); + document.body.removeEventListener('focus', this.checkConnection); + /// #endif - clearTimeout(this.checkConnectionTimeout); - this.checkConnectionTimeout = undefined; + clearTimeout(this.checkConnectionTimeout); + this.checkConnectionTimeout = undefined; + } } + + this.setConnectionStatus(offline ? ConnectionStatus.Closed : ConnectionStatus.Connected, offline ? this.checkConnectionRetryAt : undefined); } private handleSentEncryptedRequestHTTP(promise: ReturnType, message: MTMessage, noResponseMsgs: string[]) { + // let timeout = setTimeout(() => { + // this.log.error('handleSentEncryptedRequestHTTP timeout', promise, message, noResponseMsgs); + // }, 5e3); + promise.then((result) => { this.toggleOffline(false); // this.log('parse for', message); - this.parseResponse(result).then((response) => { - if(Modes.debug) { - this.log.debug('Server response', response); - } + return this.parseResponse(result).then((response) => { + this.debug && this.log.debug('Server response', response); this.processMessage(response.response, response.messageId, response.sessionId); - - noResponseMsgs.forEach((msgId) => { - if(this.sentMessages[msgId]) { - const deferred = this.sentMessages[msgId].deferred; - delete this.sentMessages[msgId]; - deferred.resolve(); - } - }); this.checkLongPoll(); - this.checkConnectionPeriod = Math.max(1.1, Math.sqrt(this.checkConnectionPeriod)); + + return true; }); }, (error) => { this.log.error('Encrypted request failed', error, message); - if(message.container) { - message.inner.forEach((msgId) => { - this.pendingMessages[msgId] = 0; - }); + this.pushResend(message.msg_id); + this.toggleOffline(true); - delete this.sentMessages[message.msg_id]; - } else { - this.pendingMessages[message.msg_id] = 0; - } - + return false; + }).then((shouldResolve) => { + // clearTimeout(timeout); noResponseMsgs.forEach((msgId) => { if(this.sentMessages[msgId]) { const deferred = this.sentMessages[msgId].deferred; delete this.sentMessages[msgId]; delete this.pendingMessages[msgId]; - deferred.reject(); + shouldResolve ? deferred.resolve() : deferred.reject(); } }); - - this.toggleOffline(true); }); } /// #endif @@ -1187,37 +1189,40 @@ export default class MTPNetworker { private sendEncryptedRequest(message: MTMessage) { return this.getEncryptedOutput(message).then(requestData => { - this.debug && this.log.debug('sendEncryptedRequest: launching message into space:', message, [message.msg_id].concat(message.inner || [])); - + this.debug && this.log.debug('sendEncryptedRequest: launching message into space:', message, [message.msg_id].concat(message.inner || []), requestData.length); const promise: Promise = this.transport.send(requestData) as any; - + // this.debug && this.log.debug('sendEncryptedRequest: launched message into space:', message, promise); + /// #if !MTPROTO_HAS_HTTP return promise; /// #else - + /// #if MTPROTO_HAS_WS if(!(this.transport instanceof HTTP)) return promise; /// #endif - + const baseError = { code: 406, type: 'NETWORK_BAD_RESPONSE', transport: this.transport }; + return promise.then((result) => { - if(!result || !result.byteLength) { - return Promise.reject(baseError); + if(!result?.byteLength) { + throw baseError; } - + + // this.debug && this.log.debug('sendEncryptedRequest: got response for:', message, [message.msg_id].concat(message.inner || [])); return result; }, (error) => { if(!error.message && !error.type) { error = Object.assign(baseError, { - type: 'NETWORK_BAD_REQUEST', - originalError: error + type: 'NETWORK_BAD_REQUEST', + originalError: error }); } - return Promise.reject(error); + + throw error; }); /// #endif }); diff --git a/src/lib/mtproto/networkerFactory.ts b/src/lib/mtproto/networkerFactory.ts index e6d965d5..f2d498b6 100644 --- a/src/lib/mtproto/networkerFactory.ts +++ b/src/lib/mtproto/networkerFactory.ts @@ -12,9 +12,9 @@ import type { ConnectionStatusChange } from "./connectionStatus"; import MTPNetworker from "./networker"; import { InvokeApiOptions } from "../../types"; -import MTTransport from "./transports/transport"; import App from "../../config/app"; import { MOUNT_CLASS_TO } from "../../config/debug"; +import { indexOfAndSplice } from "../../helpers/array"; export class NetworkerFactory { private networkers: MTPNetworker[] = []; @@ -25,19 +25,16 @@ export class NetworkerFactory { public userAgent = navigator.userAgent; public removeNetworker(networker: MTPNetworker) { - const idx = this.networkers.indexOf(networker); - if(idx !== -1) { - this.networkers.splice(idx, 1); - } + indexOfAndSplice(this.networkers, networker); } public setUpdatesProcessor(callback: (obj: any) => void) { this.updatesProcessor = callback; } - public getNetworker(dcId: number, authKey: Uint8Array, authKeyId: Uint8Array, serverSalt: Uint8Array, transport: MTTransport, options: InvokeApiOptions) { + public getNetworker(dcId: number, authKey: Uint8Array, authKeyId: Uint8Array, serverSalt: Uint8Array, options: InvokeApiOptions) { //console.log('NetworkerFactory: creating new instance of MTPNetworker:', dcId, options); - const networker = new MTPNetworker(dcId, authKey, authKeyId, serverSalt, transport, options); + const networker = new MTPNetworker(dcId, authKey, authKeyId, serverSalt, options); this.networkers.push(networker); return networker; } diff --git a/src/lib/mtproto/transports/controller.ts b/src/lib/mtproto/transports/controller.ts index 1c4aef26..d4bed753 100644 --- a/src/lib/mtproto/transports/controller.ts +++ b/src/lib/mtproto/transports/controller.ts @@ -4,22 +4,110 @@ * https://github.com/morethanwords/tweb/blob/master/LICENSE */ -import { TransportType } from "../dcConfigurator"; +import App from "../../../config/app"; +import { deferredPromise } from "../../../helpers/cancellablePromise"; +import EventListenerBase from "../../../helpers/eventListenerBase"; +import { pause } from "../../../helpers/schedulers/pause"; +import dcConfigurator, { TransportType } from "../dcConfigurator"; +import type HTTP from "./http"; +import type TcpObfuscated from "./tcpObfuscated"; +import MTTransport from "./transport"; -export class MTTransportController { +export class MTTransportController extends EventListenerBase<{ + change: (opened: MTTransportController['opened']) => void, + transport: (type: TransportType) => void +}> { private opened: Map; + private transports: {[k in TransportType]?: MTTransport}; + private pinging: boolean; constructor() { + super(true); + this.opened = new Map(); + /* this.addEventListener('change', (opened) => { + this.dispatchEvent('transport', opened.get('websocket') || !opened.get('https') ? 'websocket' : 'https'); + }); */ + + this.addEventListener('change', (opened) => { + if(!opened.get('websocket')) { + this.waitForWebSocket(); + } + }); + + setTimeout(() => { + this.waitForWebSocket(); + }, 0); } - public setTransportOpened(type: TransportType, value: boolean) { + public async pingTransports() { + const timeout = 2000; + const transports: {[k in TransportType]?: MTTransport} = this.transports = { + https: dcConfigurator.chooseServer(App.baseDcId, 'client', 'https', false), + websocket: dcConfigurator.chooseServer(App.baseDcId, 'client', 'websocket', false) + }; + + const httpPromise = deferredPromise(); + ((this.transports.https as HTTP)._send(new Uint8Array(), 'no-cors') as any as Promise) + .then(() => httpPromise.resolve(true), () => httpPromise.resolve(false)); + setTimeout(() => httpPromise.resolve(false), timeout); + + const websocketPromise = deferredPromise(); + const socket = transports.websocket as TcpObfuscated; + socket.setAutoReconnect(false); + socket.connection.addEventListener('close', () => websocketPromise.resolve(false), {once: true}); + socket.connection.addEventListener('open', () => websocketPromise.resolve(true), {once: true}); + setTimeout(() => websocketPromise.resolve(false), timeout); + + const [isHttpAvailable, isWebSocketAvailable] = await Promise.all([httpPromise, websocketPromise]); + + for(const transportType in transports) { + const transport = transports[transportType as TransportType]; + transport.destroy(); + } + + return { + https: isHttpAvailable || this.opened.get('https') > 0, + websocket: isWebSocketAvailable || this.opened.get('websocket') > 0 + }; + } + + public async waitForWebSocket() { + if(this.pinging) return; + this.pinging = true; + + while(true) { + const {https, websocket} = await this.pingTransports(); + if(https || websocket) { + this.dispatchEvent('transport', websocket || !https ? 'websocket' : 'https'); + } + + if(websocket) { + break; + } + + await pause(10000); + } + + this.pinging = false; + } + + public setTransportValue(type: TransportType, value: boolean) { let length = this.opened.get(type) || 0; - length += value ? 1 : -1; this.opened.set(type, length); + this.dispatchEvent('change', this.opened); + } + + public setTransportOpened(type: TransportType) { + return this.setTransportValue(type, true); + } + + public setTransportClosed(type: TransportType) { + return this.setTransportValue(type, false); } } -export default new MTTransportController(); \ No newline at end of file +const transportController = new MTTransportController(); +export default transportController; diff --git a/src/lib/mtproto/transports/http.ts b/src/lib/mtproto/transports/http.ts index a692e85e..b6e6802f 100644 --- a/src/lib/mtproto/transports/http.ts +++ b/src/lib/mtproto/transports/http.ts @@ -6,9 +6,14 @@ import { pause } from '../../../helpers/schedulers/pause'; import { DcId } from '../../../types'; -import { logger } from '../../logger'; +import { logger, LogTypes } from '../../logger'; import type MTPNetworker from '../networker'; import MTTransport from './transport'; +import Modes from '../../../config/modes'; + +/// #if MTPROTO_AUTO +import transportController from './controller'; +/// #endif export default class HTTP implements MTTransport { public networker: MTPNetworker; @@ -20,14 +25,28 @@ export default class HTTP implements MTTransport { body: Uint8Array }> = []; private releasing: boolean; + + public connected: boolean; + private destroyed: boolean; + private debug: boolean; constructor(protected dcId: DcId, protected url: string, logSuffix: string) { - this.log = logger(`HTTP-${dcId}` + logSuffix); + this.debug = Modes.debug && false; + + let logTypes = LogTypes.Error | LogTypes.Log; + if(this.debug) logTypes |= LogTypes.Debug; + + this.log = logger(`HTTP-${dcId}` + logSuffix, logTypes); + this.log('constructor'); + + this.connected = false; } - private _send(body: Uint8Array) { - return fetch(this.url, {method: 'POST', body}).then(response => { - if(response.status !== 200) { + public _send(body: Uint8Array, mode?: RequestMode) { + this.debug && this.log.debug('-> body length to send:', body.length); + + return fetch(this.url, {method: 'POST', body, mode}).then(response => { + if(response.status !== 200 && !mode) { response.arrayBuffer().then(buffer => { this.log.error('not 200', new TextDecoder("utf-8").decode(new Uint8Array(buffer))); @@ -36,6 +55,8 @@ export default class HTTP implements MTTransport { throw response; } + this.setConnected(true); + // * test resending by dropping random request // if(Math.random() > .5) { // throw 'asd'; @@ -44,9 +65,31 @@ export default class HTTP implements MTTransport { return response.arrayBuffer().then(buffer => { return new Uint8Array(buffer); }); + }, (err) => { + this.setConnected(false); + throw err; }); } + private setConnected(connected: boolean) { + if(this.connected === connected || this.destroyed) { + return; + } + + this.connected = connected; + + /// #if MTPROTO_AUTO + transportController.setTransportValue('https', connected); + /// #endif + } + + public destroy() { + this.setConnected(false); + this.destroyed = true; + this.pending.forEach(pending => pending.reject()); + this.pending.length = 0; + } + public send(body: Uint8Array) { if(this.networker) { return this._send(body); diff --git a/src/lib/mtproto/transports/tcpObfuscated.ts b/src/lib/mtproto/transports/tcpObfuscated.ts index 793f9380..6509c13d 100644 --- a/src/lib/mtproto/transports/tcpObfuscated.ts +++ b/src/lib/mtproto/transports/tcpObfuscated.ts @@ -12,6 +12,10 @@ import MTTransport, { MTConnection, MTConnectionConstructable } from "./transpor import intermediatePacketCodec from './intermediate'; import { ConnectionStatus } from "../connectionStatus"; +/// #if MTPROTO_AUTO +import transportController from "./controller"; +/// #endif + export default class TcpObfuscated implements MTTransport { private codec = intermediatePacketCodec; private obfuscation = new Obfuscation(); @@ -29,7 +33,7 @@ export default class TcpObfuscated implements MTTransport { private log: ReturnType; public connected = false; private lastCloseTime: number; - private connection: MTConnection; + public connection: MTConnection; private autoReconnect = true; private reconnectTimeout: number; @@ -53,6 +57,10 @@ export default class TcpObfuscated implements MTTransport { private onOpen = () => { this.connected = true; + /// #if MTPROTO_AUTO + transportController.setTransportOpened('websocket'); + /// #endif + const initPayload = this.obfuscation.init(this.codec); this.connection.send(initPayload); @@ -136,6 +144,12 @@ export default class TcpObfuscated implements MTTransport { }; public clear() { + /// #if MTPROTO_AUTO + if(this.connected) { + transportController.setTransportClosed('websocket'); + } + /// #endif + this.connected = false; if(this.connection) { @@ -183,6 +197,13 @@ export default class TcpObfuscated implements MTTransport { public destroy() { this.setAutoReconnect(false); this.close(); + + this.pending.forEach(pending => { + if(pending.reject) { + pending.reject(); + } + }); + this.pending.length = 0; } public close() { diff --git a/src/lib/mtproto/transports/transport.ts b/src/lib/mtproto/transports/transport.ts index e22b5abe..faf2bf13 100644 --- a/src/lib/mtproto/transports/transport.ts +++ b/src/lib/mtproto/transports/transport.ts @@ -10,6 +10,8 @@ import type MTPNetworker from "../networker"; export default interface MTTransport { networker: MTPNetworker; send: (data: Uint8Array) => void; + connected: boolean; + destroy: () => void; } export interface MTConnection extends EventListenerBase<{ @@ -17,7 +19,7 @@ export interface MTConnection extends EventListenerBase<{ message: (buffer: ArrayBuffer) => any, close: () => void, }> { - send: (data: Uint8Array) => void; + send: MTTransport['send']; close: () => void; } diff --git a/src/lib/mtproto/transports/websocket.ts b/src/lib/mtproto/transports/websocket.ts index c0880570..c1236f18 100644 --- a/src/lib/mtproto/transports/websocket.ts +++ b/src/lib/mtproto/transports/websocket.ts @@ -9,6 +9,9 @@ import Modes from '../../../config/modes'; import EventListenerBase from '../../../helpers/eventListenerBase'; import { MTConnection } from './transport'; +// let closeSocketBefore = Date.now() + 30e3; +// let closeSocketAfter = Date.now() + 10e3; + export default class Socket extends EventListenerBase<{ open: () => void, message: (buffer: ArrayBuffer) => any, @@ -49,6 +52,11 @@ export default class Socket extends EventListenerBase<{ this.ws.addEventListener('close', this.handleClose); this.ws.addEventListener('error', this.handleError); this.ws.addEventListener('message', this.handleMessage); + + // if(Date.now() < closeSocketBefore) { + // if(Date.now() >= closeSocketAfter) { + // this.ws.close(); + // } } public close() { diff --git a/webpack.common.js b/webpack.common.js index 2b94baf9..4aae7ee6 100644 --- a/webpack.common.js +++ b/webpack.common.js @@ -17,11 +17,17 @@ if(devMode) { console.log('DEVMODE IS ON!'); } +const MTPROTO_HTTP = false; +const MTPROTO_AUTO = true; + const opts = { MTPROTO_WORKER: true, MTPROTO_SW: false, - MTPROTO_HTTP: false, + MTPROTO_HTTP: MTPROTO_HTTP, MTPROTO_HTTP_UPLOAD: false, + MTPROTO_AUTO: MTPROTO_AUTO, // use HTTPS when WS is unavailable + MTPROTO_HAS_HTTP: MTPROTO_AUTO, + MTPROTO_HAS_WS: MTPROTO_AUTO || !MTPROTO_HTTP, DEBUG: devMode, version: 3, 'ifdef-verbose': devMode, // add this for verbose output