Telegram Web K with changes to work inside I2P
https://web.telegram.i2p/
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
128 lines
4.0 KiB
128 lines
4.0 KiB
/* |
|
* https://github.com/morethanwords/tweb |
|
* Copyright (C) 2019-2021 Eduard Kuzmenko |
|
* https://github.com/morethanwords/tweb/blob/master/LICENSE |
|
*/ |
|
|
|
import App from '../../../config/app'; |
|
import deferredPromise from '../../../helpers/cancellablePromise'; |
|
import EventListenerBase from '../../../helpers/eventListenerBase'; |
|
import pause from '../../../helpers/schedulers/pause'; |
|
import {TransportType, DcConfigurator} from '../dcConfigurator'; |
|
import type HTTP from './http'; |
|
import type TcpObfuscated from './tcpObfuscated'; |
|
import MTTransport from './transport'; |
|
|
|
export class MTTransportController extends EventListenerBase<{ |
|
change: (opened: MTTransportController['opened']) => void, |
|
transport: (type: TransportType) => void |
|
}> { |
|
private opened: Map<TransportType, number>; |
|
private transports: {[k in TransportType]?: MTTransport}; |
|
private pinging: boolean; |
|
private dcConfigurator: DcConfigurator; |
|
|
|
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(); |
|
// }, 200); // wait for first transport so won't have delay for first WS |
|
} |
|
|
|
public async pingTransports() { |
|
const dcConfigurator = this.dcConfigurator ??= new DcConfigurator(); |
|
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<boolean>(); |
|
((this.transports.https as HTTP)._send(new Uint8Array(), 'no-cors') as any as Promise<any>) |
|
.then(() => httpPromise.resolve(true), () => httpPromise.resolve(false)); |
|
setTimeout(() => httpPromise.resolve(false), timeout); |
|
|
|
const websocketPromise = deferredPromise<boolean>(); |
|
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(() => { |
|
if(websocketPromise.isFulfilled || websocketPromise.isRejected) { |
|
return; |
|
} |
|
|
|
if(socket.connection) { |
|
socket.connection.close(); |
|
} |
|
|
|
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(); |
|
} |
|
|
|
const result = { |
|
https: isHttpAvailable || this.opened.get('https') > 0, |
|
websocket: isWebSocketAvailable || this.opened.get('websocket') > 0 |
|
}; |
|
|
|
// result.websocket = false; |
|
return result; |
|
} |
|
|
|
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); |
|
} |
|
} |
|
|
|
const transportController = new MTTransportController(); |
|
export default transportController;
|
|
|