Merge branch 'http'
This commit is contained in:
commit
fbb7b3e366
@ -245,6 +245,11 @@ export default class AppMediaViewerBase<
|
|||||||
|
|
||||||
// * constructing html end
|
// * constructing html end
|
||||||
|
|
||||||
|
this.listLoader.onLoadedMore = () => {
|
||||||
|
this.buttons.prev.classList.toggle('hide', !this.listLoader.previous.length);
|
||||||
|
this.buttons.next.classList.toggle('hide', !this.listLoader.next.length);
|
||||||
|
};
|
||||||
|
|
||||||
this.setNewMover();
|
this.setNewMover();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1173,9 +1178,11 @@ export default class AppMediaViewerBase<
|
|||||||
} */
|
} */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* if(this.nextTargets.length < 10 && this.loadMore) {
|
if(this.listLoader.next.length < 10) {
|
||||||
this.loadMore();
|
setTimeout(() => {
|
||||||
} */
|
this.listLoader.load(true);
|
||||||
|
}, 0);
|
||||||
|
}
|
||||||
|
|
||||||
//if(prevTarget && (!prevTarget.parentElement || !this.isElementVisible(this.targetContainer, prevTarget))) prevTarget = null;
|
//if(prevTarget && (!prevTarget.parentElement || !this.isElementVisible(this.targetContainer, prevTarget))) prevTarget = null;
|
||||||
//if(nextTarget && (!nextTarget.parentElement || !this.isElementVisible(this.targetContainer, nextTarget))) nextTarget = null;
|
//if(nextTarget && (!nextTarget.parentElement || !this.isElementVisible(this.targetContainer, nextTarget))) nextTarget = null;
|
||||||
|
@ -101,7 +101,7 @@ export async function openAvatarViewer(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(photo) {
|
if(photo) {
|
||||||
if(!isObject(message)) {
|
if(!isObject(message) && message) {
|
||||||
photo = appPhotosManager.getPhoto(message);
|
photo = appPhotosManager.getPhoto(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
* https://github.com/morethanwords/tweb/blob/master/LICENSE
|
* https://github.com/morethanwords/tweb/blob/master/LICENSE
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
// import { IS_MOBILE_SAFARI, IS_SAFARI } from "../environment/userAgent";
|
||||||
import { cancelEvent } from "../helpers/dom/cancelEvent";
|
import { cancelEvent } from "../helpers/dom/cancelEvent";
|
||||||
import InputField, { InputFieldOptions } from "./inputField";
|
import InputField, { InputFieldOptions } from "./inputField";
|
||||||
|
|
||||||
@ -21,12 +22,15 @@ export default class PasswordInputField extends InputField {
|
|||||||
const input = this.input as HTMLInputElement;
|
const input = this.input as HTMLInputElement;
|
||||||
input.type = 'password';
|
input.type = 'password';
|
||||||
input.setAttribute('required', '');
|
input.setAttribute('required', '');
|
||||||
|
input.name = 'notsearch_password';
|
||||||
input.autocomplete = 'off';
|
input.autocomplete = 'off';
|
||||||
/* input.readOnly = true;
|
|
||||||
|
|
||||||
|
/* if(IS_SAFARI && !IS_MOBILE_SAFARI) {
|
||||||
|
input.setAttribute('readonly', '');
|
||||||
input.addEventListener('focus', () => {
|
input.addEventListener('focus', () => {
|
||||||
input.removeAttribute('readonly');
|
input.removeAttribute('readonly');
|
||||||
}, {once: true}); */
|
}, {once: true});
|
||||||
|
} */
|
||||||
|
|
||||||
// * https://stackoverflow.com/a/35949954/6758968
|
// * https://stackoverflow.com/a/35949954/6758968
|
||||||
const stealthy = document.createElement('input');
|
const stealthy = document.createElement('input');
|
||||||
|
@ -9,15 +9,30 @@
|
|||||||
* https://github.com/zhukov/webogram/blob/master/LICENSE
|
* https://github.com/zhukov/webogram/blob/master/LICENSE
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import type { TransportType } from "../lib/mtproto/dcConfigurator";
|
||||||
|
|
||||||
const Modes = {
|
const Modes = {
|
||||||
test: location.search.indexOf('test=1') > 0/* || true */,
|
test: location.search.indexOf('test=1') > 0/* || true */,
|
||||||
debug: location.search.indexOf('debug=1') > 0,
|
debug: location.search.indexOf('debug=1') > 0,
|
||||||
http: false, //location.search.indexOf('http=1') > 0,
|
http: false,
|
||||||
ssl: true, // location.search.indexOf('ssl=1') > 0 || location.protocol === 'https:' && location.search.indexOf('ssl=0') === -1,
|
ssl: true, // location.search.indexOf('ssl=1') > 0 || location.protocol === 'https:' && location.search.indexOf('ssl=0') === -1,
|
||||||
multipleConnections: true,
|
multipleConnections: true,
|
||||||
asServiceWorker: false
|
asServiceWorker: false,
|
||||||
|
transport: 'websocket' as TransportType
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// #if MTPROTO_HAS_HTTP
|
||||||
|
Modes.http = location.search.indexOf('http=1') > 0;
|
||||||
|
/// #endif
|
||||||
|
|
||||||
|
/// #if MTPROTO_HTTP || !MTPROTO_HAS_WS
|
||||||
|
Modes.http = true;
|
||||||
|
/// #endif
|
||||||
|
|
||||||
|
if(Modes.http) {
|
||||||
|
Modes.transport = 'https';
|
||||||
|
}
|
||||||
|
|
||||||
/// #if MTPROTO_SW
|
/// #if MTPROTO_SW
|
||||||
Modes.asServiceWorker = true;
|
Modes.asServiceWorker = true;
|
||||||
/// #endif
|
/// #endif
|
||||||
|
@ -17,7 +17,7 @@ export default class AvatarListLoader<Item extends {photoId: Photo.photo['id']}>
|
|||||||
loadMore: (anchor, older, loadCount) => {
|
loadMore: (anchor, older, loadCount) => {
|
||||||
if(this.peerId.isAnyChat() || !older) return Promise.resolve({count: 0, items: []}); // ! это значит, что открыло аватар чата, но следующих фотографий нет.
|
if(this.peerId.isAnyChat() || !older) return Promise.resolve({count: 0, items: []}); // ! это значит, что открыло аватар чата, но следующих фотографий нет.
|
||||||
|
|
||||||
const maxId = anchor?.photoId;
|
const maxId = anchor?.photoId || this.current?.photoId;
|
||||||
return appPhotosManager.getUserPhotos(this.peerId, maxId, loadCount).then(value => {
|
return appPhotosManager.getUserPhotos(this.peerId, maxId, loadCount).then(value => {
|
||||||
const items = value.photos.map(photoId => {
|
const items = value.photos.map(photoId => {
|
||||||
return {element: null as HTMLElement, photoId} as any;
|
return {element: null as HTMLElement, photoId} as any;
|
||||||
|
@ -47,7 +47,9 @@ export function formatDateAccordingToTodayNew(time: Date) {
|
|||||||
}).element;
|
}).element;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function formatFullSentTime(timestamp: number) {
|
export function formatFullSentTimeRaw(timestamp: number, options: {
|
||||||
|
capitalize?: boolean
|
||||||
|
} = {}) {
|
||||||
const date = new Date();
|
const date = new Date();
|
||||||
const time = new Date(timestamp * 1000);
|
const time = new Date(timestamp * 1000);
|
||||||
const now = date.getTime() / 1000;
|
const now = date.getTime() / 1000;
|
||||||
@ -56,9 +58,13 @@ export function formatFullSentTime(timestamp: number) {
|
|||||||
|
|
||||||
let dateEl: Node | string;
|
let dateEl: Node | string;
|
||||||
if((now - timestamp) < ONE_DAY && date.getDate() === time.getDate()) { // if the same day
|
if((now - timestamp) < ONE_DAY && date.getDate() === time.getDate()) { // if the same day
|
||||||
dateEl = i18n('Date.Today');
|
dateEl = i18n(options.capitalize ? 'Date.Today' : 'Peer.Status.Today');
|
||||||
} else if((now - timestamp) < (ONE_DAY * 2) && (date.getDate() - 1) === time.getDate()) { // yesterday
|
} else if((now - timestamp) < (ONE_DAY * 2) && (date.getDate() - 1) === time.getDate()) { // yesterday
|
||||||
dateEl = capitalizeFirstLetter(I18n.format('Yesterday', true));
|
dateEl = i18n(options.capitalize ? 'Yesterday' : 'Peer.Status.Yesterday');
|
||||||
|
|
||||||
|
if(options.capitalize) {
|
||||||
|
(dateEl as HTMLElement).style.textTransform = 'capitalize';
|
||||||
|
}
|
||||||
} else if(date.getFullYear() !== time.getFullYear()) { // different year
|
} else if(date.getFullYear() !== time.getFullYear()) { // different year
|
||||||
dateEl = new I18n.IntlDateElement({
|
dateEl = new I18n.IntlDateElement({
|
||||||
date: time,
|
date: time,
|
||||||
@ -80,6 +86,14 @@ export function formatFullSentTime(timestamp: number) {
|
|||||||
// dateStr = months[time.getMonth()].slice(0, 3) + ' ' + time.getDate();
|
// dateStr = months[time.getMonth()].slice(0, 3) + ' ' + time.getDate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return {dateEl, timeEl};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function formatFullSentTime(timestamp: number) {
|
||||||
|
const {dateEl, timeEl} = formatFullSentTimeRaw(timestamp, {
|
||||||
|
capitalize: true
|
||||||
|
});
|
||||||
|
|
||||||
const fragment = document.createDocumentFragment();
|
const fragment = document.createDocumentFragment();
|
||||||
fragment.append(dateEl, ' ', i18n('ScheduleController.at'), ' ', timeEl);
|
fragment.append(dateEl, ' ', i18n('ScheduleController.at'), ' ', timeEl);
|
||||||
return fragment;
|
return fragment;
|
||||||
|
@ -480,9 +480,6 @@ const lang = {
|
|||||||
"Emoji": "Emoji",
|
"Emoji": "Emoji",
|
||||||
"AddContactTitle": "Add Contact",
|
"AddContactTitle": "Add Contact",
|
||||||
"HiddenName": "Deleted Account",
|
"HiddenName": "Deleted Account",
|
||||||
"ActionGroupCallInvited": "un1 invited un2 to the voice chat",
|
|
||||||
"ActionGroupCallYouInvited": "You invited un2 to the voice chat",
|
|
||||||
"ActionGroupCallInvitedYou": "un1 invited you to the voice chat",
|
|
||||||
"Seconds": {
|
"Seconds": {
|
||||||
"one_value": "%1$d second",
|
"one_value": "%1$d second",
|
||||||
"other_value": "%1$d seconds"
|
"other_value": "%1$d seconds"
|
||||||
@ -655,6 +652,9 @@ const lang = {
|
|||||||
"Chat.Service.VoiceChatFinished": "%1$@ ended the video chat (%2$@)",
|
"Chat.Service.VoiceChatFinished": "%1$@ ended the video chat (%2$@)",
|
||||||
"Chat.Service.VoiceChatFinishedYou": "You ended the video chat (%@)",
|
"Chat.Service.VoiceChatFinishedYou": "You ended the video chat (%@)",
|
||||||
"Chat.Service.VoiceChatFinished.Channel": "Live Stream ended (%1$@)",
|
"Chat.Service.VoiceChatFinished.Channel": "Live Stream ended (%1$@)",
|
||||||
|
"Chat.Service.VoiceChatInvitation": "%1$@ invited %2$@ to the [video chat](open)",
|
||||||
|
"Chat.Service.VoiceChatInvitationByYou": "You invited %1$@ to the [video chat](open)",
|
||||||
|
"Chat.Service.VoiceChatInvitationForYou": "%1$@ invited you to the [video chat](open)",
|
||||||
"ChatList.Service.VoiceChatScheduled": "%1$@ scheduled a video chat for %2$@",
|
"ChatList.Service.VoiceChatScheduled": "%1$@ scheduled a video chat for %2$@",
|
||||||
"ChatList.Service.VoiceChatScheduledYou": "You scheduled a video chat for %2$@",
|
"ChatList.Service.VoiceChatScheduledYou": "You scheduled a video chat for %2$@",
|
||||||
"Chat.Poll.Unvote": "Retract Vote",
|
"Chat.Poll.Unvote": "Retract Vote",
|
||||||
|
@ -28,7 +28,7 @@ const lang = {
|
|||||||
"Login.ContinueOnLanguage": "Continue in English",
|
"Login.ContinueOnLanguage": "Continue in English",
|
||||||
"Login.QR.Title": "Log in to Telegram by QR Code",
|
"Login.QR.Title": "Log in to Telegram by QR Code",
|
||||||
"Login.QR.Help1": "Open Telegram on your phone",
|
"Login.QR.Help1": "Open Telegram on your phone",
|
||||||
"Login.QR.Help2": "Go to **Settings** > **Devices** > **Scan QR**",
|
"Login.QR.Help2": "Go to **Settings** > **Devices** > **Link Desktop Device**",
|
||||||
"Login.QR.Help3": "Point your phone at this screen to confirm login",
|
"Login.QR.Help3": "Point your phone at this screen to confirm login",
|
||||||
"Login.QR.Cancel": "Log in by phone Number",
|
"Login.QR.Cancel": "Log in by phone Number",
|
||||||
"Login.QR.Login": "Log in by QR Code",
|
"Login.QR.Login": "Log in by QR Code",
|
||||||
|
@ -2933,6 +2933,20 @@ export class AppMessagesManager {
|
|||||||
return el;
|
return el;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private wrapJoinVoiceChatAnchor(message: Message.messageService) {
|
||||||
|
const action = message.action as MessageAction.messageActionInviteToGroupCall;
|
||||||
|
const {onclick, url} = RichTextProcessor.wrapUrl(`tg://voicechat?chat_id=${message.peerId.toChatId()}&id=${action.call.id}&access_hash=${action.call.access_hash}`);
|
||||||
|
if(!onclick) {
|
||||||
|
return document.createElement('span');
|
||||||
|
}
|
||||||
|
|
||||||
|
const a = document.createElement('a');
|
||||||
|
a.href = url;
|
||||||
|
a.setAttribute('onclick', onclick + '(this)');
|
||||||
|
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
private wrapMessageActionTextNewUnsafe(message: MyMessage, plain?: boolean) {
|
private wrapMessageActionTextNewUnsafe(message: MyMessage, plain?: boolean) {
|
||||||
const element: HTMLElement = plain ? undefined : document.createElement('span');
|
const element: HTMLElement = plain ? undefined : document.createElement('span');
|
||||||
const action = 'action' in message && message.action;
|
const action = 'action' in message && message.action;
|
||||||
@ -2976,16 +2990,7 @@ export class AppMessagesManager {
|
|||||||
if(action.duration !== undefined) {
|
if(action.duration !== undefined) {
|
||||||
args.push(formatCallDuration(action.duration, plain));
|
args.push(formatCallDuration(action.duration, plain));
|
||||||
} else {
|
} else {
|
||||||
const {onclick, url} = RichTextProcessor.wrapUrl(`tg://voicechat?chat_id=${message.peerId.toChatId()}&id=${action.call.id}&access_hash=${action.call.access_hash}`);
|
args.push(this.wrapJoinVoiceChatAnchor(message as any));
|
||||||
if(!onclick) {
|
|
||||||
args.push(document.createElement('span'));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
const a = document.createElement('a');
|
|
||||||
a.href = url;
|
|
||||||
a.setAttribute('onclick', onclick + '(this)');
|
|
||||||
args.push(a);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
@ -2993,15 +2998,15 @@ export class AppMessagesManager {
|
|||||||
|
|
||||||
case 'messageActionInviteToGroupCall': {
|
case 'messageActionInviteToGroupCall': {
|
||||||
const peerIds = [message.fromId, action.users[0].toPeerId()];
|
const peerIds = [message.fromId, action.users[0].toPeerId()];
|
||||||
let a = 'ActionGroupCall';
|
let a = 'Chat.Service.VoiceChatInvitation';
|
||||||
const myId = appUsersManager.getSelf().id;
|
const myId = appUsersManager.getSelf().id;
|
||||||
if(peerIds[0] === myId) a += 'You';
|
if(peerIds[0] === myId) a += 'ByYou';
|
||||||
a += 'Invited';
|
else if(peerIds[1] === myId) a += 'ForYou';
|
||||||
if(peerIds[1] === myId) a += 'You';
|
|
||||||
indexOfAndSplice(peerIds, myId);
|
indexOfAndSplice(peerIds, myId);
|
||||||
|
|
||||||
langPackKey = a as LangPackKey;
|
langPackKey = a as LangPackKey;
|
||||||
args = peerIds.map(peerId => getNameDivHTML(peerId, plain));
|
args = peerIds.map(peerId => getNameDivHTML(peerId, plain));
|
||||||
|
args.push(this.wrapJoinVoiceChatAnchor(message as any));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@ import { filterUnique, indexOfAndSplice } from "../../helpers/array";
|
|||||||
import { CancellablePromise, deferredPromise } from "../../helpers/cancellablePromise";
|
import { CancellablePromise, deferredPromise } from "../../helpers/cancellablePromise";
|
||||||
import cleanSearchText from "../../helpers/cleanSearchText";
|
import cleanSearchText from "../../helpers/cleanSearchText";
|
||||||
import cleanUsername from "../../helpers/cleanUsername";
|
import cleanUsername from "../../helpers/cleanUsername";
|
||||||
import { tsNow } from "../../helpers/date";
|
import { formatFullSentTimeRaw, tsNow } from "../../helpers/date";
|
||||||
import { formatPhoneNumber } from "../../helpers/formatPhoneNumber";
|
import { formatPhoneNumber } from "../../helpers/formatPhoneNumber";
|
||||||
import { safeReplaceObject, isObject } from "../../helpers/object";
|
import { safeReplaceObject, isObject } from "../../helpers/object";
|
||||||
import { Chat, InputContact, InputMedia, InputPeer, InputUser, User as MTUser, UserProfilePhoto, UserStatus } from "../../layer";
|
import { Chat, InputContact, InputMedia, InputPeer, InputUser, User as MTUser, UserProfilePhoto, UserStatus } from "../../layer";
|
||||||
@ -581,23 +581,24 @@ export class AppUsersManager {
|
|||||||
|
|
||||||
case 'userStatusOffline': {
|
case 'userStatusOffline': {
|
||||||
const date = user.status.was_online;
|
const date = user.status.was_online;
|
||||||
const now = Date.now() / 1000;
|
const today = new Date();
|
||||||
|
const now = today.getTime() / 1000 | 0;
|
||||||
|
|
||||||
if((now - date) < 60) {
|
const diff = now - date;
|
||||||
|
if(diff < 60) {
|
||||||
key = 'Peer.Status.justNow';
|
key = 'Peer.Status.justNow';
|
||||||
} else if((now - date) < 3600) {
|
} else if(diff < 3600) {
|
||||||
key = 'Peer.Status.minAgo';
|
key = 'Peer.Status.minAgo';
|
||||||
const c = (now - date) / 60 | 0;
|
const c = diff / 60 | 0;
|
||||||
args = [c];
|
args = [c];
|
||||||
} else if(now - date < 86400) {
|
} else if(diff < 86400 && today.getDate() === new Date(date * 1000).getDate()) {
|
||||||
key = 'LastSeen.HoursAgo';
|
key = 'LastSeen.HoursAgo';
|
||||||
const c = (now - date) / 3600 | 0;
|
const c = diff / 3600 | 0;
|
||||||
args = [c];
|
args = [c];
|
||||||
} else {
|
} else {
|
||||||
key = 'Peer.Status.LastSeenAt';
|
key = 'Peer.Status.LastSeenAt';
|
||||||
const d = new Date(date * 1000);
|
const {dateEl, timeEl} = formatFullSentTimeRaw(date);
|
||||||
args = [('0' + d.getDate()).slice(-2) + '.' + ('0' + (d.getMonth() + 1)).slice(-2),
|
args = [dateEl, timeEl];
|
||||||
('0' + d.getHours()).slice(-2) + ':' + ('0' + d.getMinutes()).slice(-2)];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
@ -30,11 +30,16 @@ import IDBStorage from '../idb';
|
|||||||
import CryptoWorker from "../crypto/cryptoworker";
|
import CryptoWorker from "../crypto/cryptoworker";
|
||||||
import ctx from '../../environment/ctx';
|
import ctx from '../../environment/ctx';
|
||||||
import noop from '../../helpers/noop';
|
import noop from '../../helpers/noop';
|
||||||
|
import Modes from '../../config/modes';
|
||||||
|
|
||||||
/// #if !MTPROTO_WORKER
|
/// #if !MTPROTO_WORKER
|
||||||
import rootScope from '../rootScope';
|
import rootScope from '../rootScope';
|
||||||
/// #endif
|
/// #endif
|
||||||
|
|
||||||
|
/// #if MTPROTO_AUTO
|
||||||
|
import transportController from './transports/controller';
|
||||||
|
/// #endif
|
||||||
|
|
||||||
/* var networker = apiManager.cachedNetworkers.websocket.upload[2];
|
/* var networker = apiManager.cachedNetworkers.websocket.upload[2];
|
||||||
networker.wrapMtpMessage({
|
networker.wrapMtpMessage({
|
||||||
_: 'msgs_state_req',
|
_: 'msgs_state_req',
|
||||||
@ -73,25 +78,45 @@ export class ApiManager {
|
|||||||
private cachedNetworkers: {
|
private cachedNetworkers: {
|
||||||
[transportType in TransportType]: {
|
[transportType in TransportType]: {
|
||||||
[connectionType in ConnectionType]: {
|
[connectionType in ConnectionType]: {
|
||||||
[dcId: number]: MTPNetworker[]
|
[dcId: DcId]: MTPNetworker[]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} = {} as any;
|
};
|
||||||
|
|
||||||
private cachedExportPromise: {[x: number]: Promise<unknown>} = {};
|
private cachedExportPromise: {[x: number]: Promise<unknown>};
|
||||||
private gettingNetworkers: {[dcIdAndType: string]: Promise<MTPNetworker>} = {};
|
private gettingNetworkers: {[dcIdAndType: string]: Promise<MTPNetworker>};
|
||||||
private baseDcId: DcId = 0 as DcId;
|
private baseDcId: DcId;
|
||||||
|
|
||||||
//public telegramMeNotified = false;
|
//public telegramMeNotified = false;
|
||||||
|
|
||||||
private log: ReturnType<typeof logger> = logger('API');
|
private log: ReturnType<typeof logger>;
|
||||||
|
|
||||||
private afterMessageTempIds: {
|
private afterMessageTempIds: {
|
||||||
[tempId: string]: {
|
[tempId: string]: {
|
||||||
messageId: string,
|
messageId: string,
|
||||||
promise: Promise<any>
|
promise: Promise<any>
|
||||||
}
|
}
|
||||||
} = {};
|
};
|
||||||
|
|
||||||
|
private transportType: TransportType;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.log = logger('API');
|
||||||
|
|
||||||
|
this.cachedNetworkers = {} as any;
|
||||||
|
this.cachedExportPromise = {};
|
||||||
|
this.gettingNetworkers = {};
|
||||||
|
this.baseDcId = 0;
|
||||||
|
this.afterMessageTempIds = {};
|
||||||
|
|
||||||
|
this.transportType = Modes.transport;
|
||||||
|
|
||||||
|
/// #if MTPROTO_AUTO
|
||||||
|
transportController.addEventListener('transport', (transportType) => {
|
||||||
|
this.changeTransportType(transportType);
|
||||||
|
});
|
||||||
|
/// #endif
|
||||||
|
}
|
||||||
|
|
||||||
//private lol = false;
|
//private lol = false;
|
||||||
|
|
||||||
@ -112,6 +137,67 @@ export class ApiManager {
|
|||||||
}
|
}
|
||||||
} */
|
} */
|
||||||
|
|
||||||
|
private getTransportType(connectionType: ConnectionType) {
|
||||||
|
/// #if MTPROTO_HTTP_UPLOAD
|
||||||
|
// @ts-ignore
|
||||||
|
const transportType: TransportType = connectionType === 'upload' && IS_SAFARI ? 'https' : 'websocket';
|
||||||
|
//const transportType: TransportType = connectionType !== 'client' ? 'https' : 'websocket';
|
||||||
|
/// #else
|
||||||
|
// @ts-ignore
|
||||||
|
const transportType: TransportType = this.transportType;
|
||||||
|
/// #endif
|
||||||
|
|
||||||
|
return transportType;
|
||||||
|
}
|
||||||
|
|
||||||
|
private iterateNetworkers(callback: (o: {networker: MTPNetworker, dcId: DcId, connectionType: ConnectionType, transportType: TransportType, index: number, array: MTPNetworker[]}) => void) {
|
||||||
|
for(const transportType in this.cachedNetworkers) {
|
||||||
|
const connections = this.cachedNetworkers[transportType as TransportType];
|
||||||
|
for(const connectionType in connections) {
|
||||||
|
const dcs = connections[connectionType as ConnectionType];
|
||||||
|
for(const dcId in dcs) {
|
||||||
|
const networkers = dcs[dcId as any as DcId];
|
||||||
|
networkers.forEach((networker, idx, arr) => {
|
||||||
|
callback({
|
||||||
|
networker,
|
||||||
|
dcId: +dcId as DcId,
|
||||||
|
connectionType: connectionType as ConnectionType,
|
||||||
|
transportType: transportType as TransportType,
|
||||||
|
index: idx,
|
||||||
|
array: arr
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private chooseServer(dcId: DcId, connectionType: ConnectionType, transportType: TransportType) {
|
||||||
|
return dcConfigurator.chooseServer(dcId, connectionType, transportType, connectionType === 'client');
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
this.cachedNetworkers[oldTransportType] = newObject;
|
||||||
|
|
||||||
|
this.transportType = transportType;
|
||||||
|
|
||||||
|
this.iterateNetworkers((info) => {
|
||||||
|
const transportType = this.getTransportType(info.connectionType);
|
||||||
|
const transport = this.chooseServer(info.dcId, info.connectionType, transportType);
|
||||||
|
info.networker.changeTransport(transport);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
public async getBaseDcId() {
|
public async getBaseDcId() {
|
||||||
if(this.baseDcId) {
|
if(this.baseDcId) {
|
||||||
return this.baseDcId;
|
return this.baseDcId;
|
||||||
@ -203,16 +289,8 @@ export class ApiManager {
|
|||||||
const connectionType: ConnectionType = options.fileDownload ? 'download' : (options.fileUpload ? 'upload' : 'client');
|
const connectionType: ConnectionType = options.fileDownload ? 'download' : (options.fileUpload ? 'upload' : 'client');
|
||||||
//const connectionType: ConnectionType = 'client';
|
//const connectionType: ConnectionType = 'client';
|
||||||
|
|
||||||
/// #if MTPROTO_HTTP_UPLOAD
|
const transportType = this.getTransportType(connectionType);
|
||||||
// @ts-ignore
|
if(!this.cachedNetworkers[transportType]) {
|
||||||
const transportType: TransportType = connectionType === 'upload' && IS_SAFARI ? 'https' : 'websocket';
|
|
||||||
//const transportType: TransportType = connectionType !== 'client' ? 'https' : 'websocket';
|
|
||||||
/// #else
|
|
||||||
// @ts-ignore
|
|
||||||
const transportType = 'websocket';
|
|
||||||
/// #endif
|
|
||||||
|
|
||||||
if(!this.cachedNetworkers.hasOwnProperty(transportType)) {
|
|
||||||
this.cachedNetworkers[transportType] = {
|
this.cachedNetworkers[transportType] = {
|
||||||
client: {},
|
client: {},
|
||||||
download: {},
|
download: {},
|
||||||
@ -250,9 +328,9 @@ export class ApiManager {
|
|||||||
const ak: DcAuthKey = `dc${dcId}_auth_key` as any;
|
const ak: DcAuthKey = `dc${dcId}_auth_key` as any;
|
||||||
const ss: DcServerSalt = `dc${dcId}_server_salt` as any;
|
const ss: DcServerSalt = `dc${dcId}_server_salt` as any;
|
||||||
|
|
||||||
|
let transport = this.chooseServer(dcId, connectionType, transportType);
|
||||||
return this.gettingNetworkers[getKey] = Promise.all([ak, ss].map(key => sessionStorage.get(key)))
|
return this.gettingNetworkers[getKey] = Promise.all([ak, ss].map(key => sessionStorage.get(key)))
|
||||||
.then(async([authKeyHex, serverSaltHex]) => {
|
.then(async([authKeyHex, serverSaltHex]) => {
|
||||||
const transport = dcConfigurator.chooseServer(dcId, connectionType, transportType, connectionType === 'client');
|
|
||||||
let networker: MTPNetworker;
|
let networker: MTPNetworker;
|
||||||
if(authKeyHex && authKeyHex.length === 512) {
|
if(authKeyHex && authKeyHex.length === 512) {
|
||||||
if(!serverSaltHex || serverSaltHex.length !== 16) {
|
if(!serverSaltHex || serverSaltHex.length !== 16) {
|
||||||
@ -263,19 +341,17 @@ export class ApiManager {
|
|||||||
const authKeyId = (await CryptoWorker.invokeCrypto('sha1-hash', authKey)).slice(-8);
|
const authKeyId = (await CryptoWorker.invokeCrypto('sha1-hash', authKey)).slice(-8);
|
||||||
const serverSalt = bytesFromHex(serverSaltHex);
|
const serverSalt = bytesFromHex(serverSaltHex);
|
||||||
|
|
||||||
networker = networkerFactory.getNetworker(dcId, authKey, authKeyId, serverSalt, transport, options);
|
networker = networkerFactory.getNetworker(dcId, authKey, authKeyId, serverSalt, options);
|
||||||
} else {
|
} else {
|
||||||
try { // if no saved state
|
try { // if no saved state
|
||||||
const auth = await authorizer.auth(dcId);
|
const auth = await authorizer.auth(dcId);
|
||||||
|
|
||||||
const storeObj = {
|
sessionStorage.set({
|
||||||
[ak]: bytesToHex(auth.authKey),
|
[ak]: bytesToHex(auth.authKey),
|
||||||
[ss]: bytesToHex(auth.serverSalt)
|
[ss]: bytesToHex(auth.serverSalt)
|
||||||
};
|
});
|
||||||
|
|
||||||
sessionStorage.set(storeObj);
|
networker = networkerFactory.getNetworker(dcId, auth.authKey, auth.authKeyId, auth.serverSalt, options);
|
||||||
|
|
||||||
networker = networkerFactory.getNetworker(dcId, auth.authKey, auth.authKeyId, auth.serverSalt, transport, options);
|
|
||||||
} catch(error) {
|
} catch(error) {
|
||||||
this.log('Get networker error', error, (error as Error).stack);
|
this.log('Get networker error', error, (error as Error).stack);
|
||||||
delete this.gettingNetworkers[getKey];
|
delete this.gettingNetworkers[getKey];
|
||||||
@ -283,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) => {
|
/* networker.onConnectionStatusChange = (online) => {
|
||||||
console.log('status:', online);
|
console.log('status:', online);
|
||||||
}; */
|
}; */
|
||||||
@ -315,7 +401,6 @@ export class ApiManager {
|
|||||||
networker.destroy();
|
networker.destroy();
|
||||||
networkerFactory.removeNetworker(networker);
|
networkerFactory.removeNetworker(networker);
|
||||||
DcConfigurator.removeTransport(this.cachedNetworkers, networker);
|
DcConfigurator.removeTransport(this.cachedNetworkers, networker);
|
||||||
DcConfigurator.removeTransport(dcConfigurator.chosenServers, networker.transport);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
networker.setDrainTimeout();
|
networker.setDrainTimeout();
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { TLSerialization, TLDeserialization } from "./tl_utils";
|
import { TLSerialization, TLDeserialization } from "./tl_utils";
|
||||||
import dcConfigurator from "./dcConfigurator";
|
import dcConfigurator, { TransportType } from "./dcConfigurator";
|
||||||
import rsaKeysManager from "./rsaKeysManager";
|
import rsaKeysManager from "./rsaKeysManager";
|
||||||
import timeManager from "./timeManager";
|
import timeManager from "./timeManager";
|
||||||
|
|
||||||
@ -21,7 +21,12 @@ import { bytesCmp, bytesToHex, bytesFromHex, bytesXor } from "../../helpers/byte
|
|||||||
import DEBUG from "../../config/debug";
|
import DEBUG from "../../config/debug";
|
||||||
import { cmp, int2bigInt, one, pow, str2bigInt, sub } from "../../vendor/leemon";
|
import { cmp, int2bigInt, one, pow, str2bigInt, sub } from "../../vendor/leemon";
|
||||||
import { addPadding } from "./bin_utils";
|
import { addPadding } from "./bin_utils";
|
||||||
import { Awaited } from "../../types";
|
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 fNewNonce: any = bytesFromHex('8761970c24cb2329b5b2459752c502f3057cb7e8dbab200e526e8767fdc73b3c').reverse();
|
||||||
let fNonce: any = bytesFromHex('b597720d11faa5914ef485c529cde414').reverse();
|
let fNonce: any = bytesFromHex('b597720d11faa5914ef485c529cde414').reverse();
|
||||||
@ -66,8 +71,6 @@ type AuthOptions = {
|
|||||||
|
|
||||||
localTime?: number,
|
localTime?: number,
|
||||||
serverTime?: any,
|
serverTime?: any,
|
||||||
|
|
||||||
localTry?: number
|
|
||||||
};
|
};
|
||||||
|
|
||||||
type ResPQ = {
|
type ResPQ = {
|
||||||
@ -100,16 +103,23 @@ type req_DH_params = {
|
|||||||
|
|
||||||
export class Authorizer {
|
export class Authorizer {
|
||||||
private cached: {
|
private cached: {
|
||||||
[dcId: number]: Promise<AuthOptions>
|
[dcId: DcId]: Promise<AuthOptions>
|
||||||
} = {};
|
};
|
||||||
|
|
||||||
private log: ReturnType<typeof logger>;
|
private log: ReturnType<typeof logger>;
|
||||||
|
|
||||||
|
private transportType: TransportType;
|
||||||
|
|
||||||
|
/// #if MTPROTO_AUTO
|
||||||
|
private getTransportTypePromise: Promise<void>;
|
||||||
|
/// #endif
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
|
this.cached = {};
|
||||||
this.log = logger(`AUTHORIZER`, LogTypes.Error | LogTypes.Log);
|
this.log = logger(`AUTHORIZER`, LogTypes.Error | LogTypes.Log);
|
||||||
}
|
}
|
||||||
|
|
||||||
private sendPlainRequest(dcId: number, requestArray: Uint8Array) {
|
private sendPlainRequest(dcId: DcId, requestArray: Uint8Array) {
|
||||||
const requestLength = requestArray.byteLength;
|
const requestLength = requestArray.byteLength;
|
||||||
|
|
||||||
const header = new TLSerialization();
|
const header = new TLSerialization();
|
||||||
@ -122,7 +132,7 @@ export class Authorizer {
|
|||||||
resultArray.set(headerArray);
|
resultArray.set(headerArray);
|
||||||
resultArray.set(requestArray, headerArray.length);
|
resultArray.set(requestArray, headerArray.length);
|
||||||
|
|
||||||
const transport = dcConfigurator.chooseServer(dcId);
|
const transport = dcConfigurator.chooseServer(dcId, 'client', this.transportType);
|
||||||
const baseError = {
|
const baseError = {
|
||||||
code: 406,
|
code: 406,
|
||||||
type: 'NETWORK_BAD_RESPONSE'
|
type: 'NETWORK_BAD_RESPONSE'
|
||||||
@ -571,39 +581,47 @@ export class Authorizer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async auth(dcId: number): Promise<AuthOptions> {
|
/// #if MTPROTO_AUTO
|
||||||
if(dcId in this.cached) {
|
private getTransportType() {
|
||||||
return this.cached[dcId];
|
if(this.getTransportTypePromise) return this.getTransportTypePromise;
|
||||||
}
|
return this.getTransportTypePromise = transportController.pingTransports().then(({websocket}) => {
|
||||||
|
this.transportType = websocket ? 'websocket' : 'https';
|
||||||
const nonce = /* fNonce ? fNonce : */new Uint8Array(16).randomize();
|
|
||||||
/* const nonce = new Array(16);
|
|
||||||
MTProto.secureRandom.nextBytes(nonce); */
|
|
||||||
|
|
||||||
if(!dcConfigurator.chooseServer(dcId)) {
|
|
||||||
throw new Error('[MT] No server found for dc ' + dcId);
|
|
||||||
}
|
|
||||||
|
|
||||||
// await new Promise((resolve) => setTimeout(resolve, 2e3));
|
|
||||||
|
|
||||||
const auth: AuthOptions = {dcId, nonce, localTry: 1};
|
|
||||||
|
|
||||||
try {
|
|
||||||
const promise = this.sendReqPQ(auth);
|
|
||||||
this.cached[dcId] = promise;
|
|
||||||
return await promise;
|
|
||||||
} catch(err) {
|
|
||||||
if(/* err.originalError === -404 && */auth.localTry <= 3) {
|
|
||||||
return this.sendReqPQ({
|
|
||||||
dcId: auth.dcId,
|
|
||||||
nonce: new Uint8Array(16).randomize(),
|
|
||||||
localTry: auth.localTry + 1
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
/// #endif
|
||||||
|
|
||||||
delete this.cached[dcId];
|
public auth(dcId: DcId) {
|
||||||
throw err;
|
let promise = this.cached[dcId];
|
||||||
|
if(promise) {
|
||||||
|
return promise;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
promise = new Promise(async(resolve, reject) => {
|
||||||
|
/// #if MTPROTO_AUTO
|
||||||
|
await this.getTransportType();
|
||||||
|
/// #endif
|
||||||
|
|
||||||
|
let error: ApiError;
|
||||||
|
let _try = 1;
|
||||||
|
while(_try++ <= 3) {
|
||||||
|
try {
|
||||||
|
const auth: AuthOptions = {
|
||||||
|
dcId,
|
||||||
|
nonce: new Uint8Array(16).randomize()
|
||||||
|
};
|
||||||
|
|
||||||
|
const promise = this.sendReqPQ(auth);
|
||||||
|
resolve(await promise);
|
||||||
|
return;
|
||||||
|
} catch(err) {
|
||||||
|
error = err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
reject(error);
|
||||||
|
});
|
||||||
|
|
||||||
|
return this.cached[dcId] = promise;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,18 +11,20 @@
|
|||||||
|
|
||||||
import MTTransport, { MTConnectionConstructable } from './transports/transport';
|
import MTTransport, { MTConnectionConstructable } from './transports/transport';
|
||||||
import Modes from '../../config/modes';
|
import Modes from '../../config/modes';
|
||||||
|
import { indexOfAndSplice } from '../../helpers/array';
|
||||||
|
import App from '../../config/app';
|
||||||
|
|
||||||
/// #if MTPROTO_HTTP || MTPROTO_HTTP_UPLOAD
|
/// #if MTPROTO_HAS_HTTP
|
||||||
import HTTP from './transports/http';
|
import HTTP from './transports/http';
|
||||||
/// #endif
|
/// #endif
|
||||||
|
|
||||||
/// #if !MTPROTO_HTTP
|
/// #if MTPROTO_HAS_WS
|
||||||
import Socket from './transports/websocket';
|
import Socket from './transports/websocket';
|
||||||
import TcpObfuscated from './transports/tcpObfuscated';
|
import TcpObfuscated from './transports/tcpObfuscated';
|
||||||
import { IS_SAFARI } from '../../environment/userAgent';
|
import { IS_SAFARI } from '../../environment/userAgent';
|
||||||
import { IS_WEB_WORKER } from '../../helpers/context';
|
import { IS_WEB_WORKER } from '../../helpers/context';
|
||||||
import SocketProxied from './transports/socketProxied';
|
import SocketProxied from './transports/socketProxied';
|
||||||
import App from '../../config/app';
|
import { DcId } from '../../types';
|
||||||
/// #endif
|
/// #endif
|
||||||
|
|
||||||
export type TransportType = 'websocket' | 'https' | 'http';
|
export type TransportType = 'websocket' | 'https' | 'http';
|
||||||
@ -30,7 +32,7 @@ export type ConnectionType = 'client' | 'download' | 'upload';
|
|||||||
type Servers = {
|
type Servers = {
|
||||||
[transportType in TransportType]: {
|
[transportType in TransportType]: {
|
||||||
[connectionType in ConnectionType]: {
|
[connectionType in ConnectionType]: {
|
||||||
[dcId: number]: MTTransport[]
|
[dcId: DcId]: MTTransport[]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -56,8 +58,8 @@ export class DcConfigurator {
|
|||||||
|
|
||||||
public chosenServers: Servers = {} as any;
|
public chosenServers: Servers = {} as any;
|
||||||
|
|
||||||
/// #if !MTPROTO_HTTP
|
/// #if MTPROTO_HAS_WS
|
||||||
private transportSocket = (dcId: number, connectionType: ConnectionType, suffix: string) => {
|
private transportSocket = (dcId: DcId, connectionType: ConnectionType, suffix: string) => {
|
||||||
const path = 'apiws' + TEST_SUFFIX;
|
const path = 'apiws' + TEST_SUFFIX;
|
||||||
const chosenServer = `wss://${App.suffix.toLowerCase()}ws${dcId}${suffix}.web.telegram.org/${path}`;
|
const chosenServer = `wss://${App.suffix.toLowerCase()}ws${dcId}${suffix}.web.telegram.org/${path}`;
|
||||||
const logSuffix = connectionType === 'upload' ? '-U' : connectionType === 'download' ? '-D' : '';
|
const logSuffix = connectionType === 'upload' ? '-U' : connectionType === 'download' ? '-D' : '';
|
||||||
@ -70,25 +72,33 @@ export class DcConfigurator {
|
|||||||
};
|
};
|
||||||
/// #endif
|
/// #endif
|
||||||
|
|
||||||
/// #if MTPROTO_HTTP_UPLOAD || MTPROTO_HTTP
|
/// #if MTPROTO_HAS_HTTP
|
||||||
private transportHTTP = (dcId: number, connectionType: ConnectionType, suffix: string) => {
|
private transportHTTP = (dcId: DcId, connectionType: ConnectionType, suffix: string) => {
|
||||||
|
let chosenServer: string;
|
||||||
if(Modes.ssl || !Modes.http) {
|
if(Modes.ssl || !Modes.http) {
|
||||||
const subdomain = this.sslSubdomains[dcId - 1] + (connectionType !== 'client' ? '-1' : '');
|
const subdomain = this.sslSubdomains[dcId - 1] + (connectionType !== 'client' ? '-1' : '');
|
||||||
const path = Modes.test ? 'apiw_test1' : 'apiw1';
|
const path = Modes.test ? 'apiw_test1' : 'apiw1';
|
||||||
const chosenServer = 'https://' + subdomain + '.web.telegram.org/' + path;
|
chosenServer = 'https://' + subdomain + '.web.telegram.org/' + path;
|
||||||
return new HTTP(dcId, chosenServer);
|
|
||||||
} else {
|
} else {
|
||||||
for(let dcOption of this.dcOptions) {
|
for(let dcOption of this.dcOptions) {
|
||||||
if(dcOption.id === dcId) {
|
if(dcOption.id === dcId) {
|
||||||
const chosenServer = 'http://' + dcOption.host + (dcOption.port !== 80 ? ':' + dcOption.port : '') + '/apiw1';
|
chosenServer = 'http://' + dcOption.host + (dcOption.port !== 80 ? ':' + dcOption.port : '') + '/apiw1';
|
||||||
return new HTTP(dcId, chosenServer);
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const logSuffix = connectionType === 'upload' ? '-U' : connectionType === 'download' ? '-D' : '';
|
||||||
|
return new HTTP(dcId, chosenServer, logSuffix);
|
||||||
};
|
};
|
||||||
/// #endif
|
/// #endif
|
||||||
|
|
||||||
public chooseServer(dcId: number, connectionType: ConnectionType = 'client', transportType: TransportType = 'websocket', reuse = true) {
|
public chooseServer(
|
||||||
|
dcId: DcId,
|
||||||
|
connectionType: ConnectionType = 'client',
|
||||||
|
transportType: TransportType = Modes.transport,
|
||||||
|
reuse = true
|
||||||
|
) {
|
||||||
/* if(transportType === 'websocket' && !Modes.multipleConnections) {
|
/* if(transportType === 'websocket' && !Modes.multipleConnections) {
|
||||||
connectionType = 'client';
|
connectionType = 'client';
|
||||||
} */
|
} */
|
||||||
@ -114,7 +124,7 @@ export class DcConfigurator {
|
|||||||
|
|
||||||
const suffix = connectionType === 'client' ? '' : '-1';
|
const suffix = connectionType === 'client' ? '' : '-1';
|
||||||
|
|
||||||
/// #if MTPROTO_HTTP_UPLOAD
|
/// #if MTPROTO_HAS_WS && MTPROTO_HAS_HTTP
|
||||||
transport = (transportType === 'websocket' ? this.transportSocket : this.transportHTTP)(dcId, connectionType, suffix);
|
transport = (transportType === 'websocket' ? this.transportSocket : this.transportHTTP)(dcId, connectionType, suffix);
|
||||||
/// #elif !MTPROTO_HTTP
|
/// #elif !MTPROTO_HTTP
|
||||||
transport = this.transportSocket(dcId, connectionType, suffix);
|
transport = this.transportSocket(dcId, connectionType, suffix);
|
||||||
@ -145,10 +155,7 @@ export class DcConfigurator {
|
|||||||
for(const dcId in obj[transportType][connectionType]) {
|
for(const dcId in obj[transportType][connectionType]) {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const transports: T[] = obj[transportType][connectionType][dcId];
|
const transports: T[] = obj[transportType][connectionType][dcId];
|
||||||
const idx = transports.indexOf(transport);
|
indexOfAndSplice(transports, transport);
|
||||||
if(idx !== -1) {
|
|
||||||
transports.splice(idx, 1);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,7 @@ import DEBUG from '../../config/debug';
|
|||||||
import Modes from '../../config/modes';
|
import Modes from '../../config/modes';
|
||||||
import noop from '../../helpers/noop';
|
import noop from '../../helpers/noop';
|
||||||
|
|
||||||
/// #if MTPROTO_HTTP_UPLOAD || MTPROTO_HTTP
|
/// #if MTPROTO_HAS_HTTP
|
||||||
import HTTP from './transports/http';
|
import HTTP from './transports/http';
|
||||||
/// #endif
|
/// #endif
|
||||||
|
|
||||||
@ -35,6 +35,8 @@ import type TcpObfuscated from './transports/tcpObfuscated';
|
|||||||
import { bigInt2str, rightShift_, str2bigInt } from '../../vendor/leemon';
|
import { bigInt2str, rightShift_, str2bigInt } from '../../vendor/leemon';
|
||||||
import { forEachReverse } from '../../helpers/array';
|
import { forEachReverse } from '../../helpers/array';
|
||||||
import { ConnectionStatus } from './connectionStatus';
|
import { ConnectionStatus } from './connectionStatus';
|
||||||
|
import ctx from '../../environment/ctx';
|
||||||
|
import dcConfigurator, { DcConfigurator } from './dcConfigurator';
|
||||||
|
|
||||||
//console.error('networker included!', new Error().stack);
|
//console.error('networker included!', new Error().stack);
|
||||||
|
|
||||||
@ -95,21 +97,23 @@ export default class MTPNetworker {
|
|||||||
private pendingMessages: {[msgId: MTLong]: number} = {};
|
private pendingMessages: {[msgId: MTLong]: number} = {};
|
||||||
private pendingAcks: Array<MTLong> = [];
|
private pendingAcks: Array<MTLong> = [];
|
||||||
private pendingResends: Array<MTLong> = [];
|
private pendingResends: Array<MTLong> = [];
|
||||||
public connectionInited = false;
|
public connectionInited: boolean;
|
||||||
|
|
||||||
private nextReqTimeout: number;
|
private nextReqTimeout: number;
|
||||||
private nextReq: number = 0;
|
private nextReq: number = 0;
|
||||||
|
|
||||||
/// #if MTPROTO_HTTP || MTPROTO_HTTP_UPLOAD
|
/// #if MTPROTO_HAS_HTTP
|
||||||
//private longPollInt: number;
|
private longPollInterval: number;
|
||||||
private longPollPending = 0;
|
private longPollPending: number;
|
||||||
|
private checkConnectionRetryAt: number;
|
||||||
private checkConnectionTimeout: number;
|
private checkConnectionTimeout: number;
|
||||||
private checkConnectionPeriod = 0;
|
private checkConnectionPeriod = 0;
|
||||||
private sleepAfter = 0;
|
private sleepAfter: number;
|
||||||
private offline = false;
|
private offline = false;
|
||||||
|
private sendingLongPoll: boolean;
|
||||||
/// #endif
|
/// #endif
|
||||||
|
|
||||||
private seqNo: number = 0;
|
private seqNo: number;
|
||||||
private prevSessionId: Uint8Array;
|
private prevSessionId: Uint8Array;
|
||||||
private sessionId: Uint8Array;
|
private sessionId: Uint8Array;
|
||||||
private serverSalt: Uint8Array;
|
private serverSalt: Uint8Array;
|
||||||
@ -133,14 +137,21 @@ export default class MTPNetworker {
|
|||||||
public onDrain: () => void;
|
public onDrain: () => void;
|
||||||
private onDrainTimeout: number;
|
private onDrainTimeout: number;
|
||||||
|
|
||||||
|
public transport: MTTransport;
|
||||||
|
|
||||||
//private disconnectDelay: number;
|
//private disconnectDelay: number;
|
||||||
//private pingPromise: CancellablePromise<any>;
|
//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: Uint8Array, private authKeyId: Uint8Array,
|
constructor(
|
||||||
serverSalt: Uint8Array, public transport: MTTransport, options: InvokeApiOptions = {}) {
|
public dcId: number,
|
||||||
|
private authKey: Uint8Array,
|
||||||
|
private authKeyId: Uint8Array,
|
||||||
|
serverSalt: Uint8Array,
|
||||||
|
options: InvokeApiOptions = {}
|
||||||
|
) {
|
||||||
this.authKeyUint8 = convertToUint8Array(this.authKey);
|
this.authKeyUint8 = convertToUint8Array(this.authKey);
|
||||||
this.serverSalt = convertToUint8Array(serverSalt);
|
this.serverSalt = convertToUint8Array(serverSalt);
|
||||||
|
|
||||||
@ -151,7 +162,7 @@ export default class MTPNetworker {
|
|||||||
const suffix = this.isFileUpload ? '-U' : this.isFileDownload ? '-D' : '';
|
const suffix = this.isFileUpload ? '-U' : this.isFileDownload ? '-D' : '';
|
||||||
this.name = 'NET-' + dcId + suffix;
|
this.name = 'NET-' + dcId + suffix;
|
||||||
//this.log = logger(this.name, this.upload && this.dcId === 2 ? LogLevels.debug | LogLevels.warn | LogLevels.log | LogLevels.error : LogLevels.error);
|
//this.log = logger(this.name, this.upload && this.dcId === 2 ? LogLevels.debug | LogLevels.warn | LogLevels.log | LogLevels.error : LogLevels.error);
|
||||||
this.log = logger(this.name, LogTypes.Log | /* LogTypes.Debug | */LogTypes.Error | LogTypes.Warn, undefined);
|
this.log = logger(this.name, LogTypes.Log | LogTypes.Debug | LogTypes.Error | LogTypes.Warn, undefined);
|
||||||
this.log('constructor'/* , this.authKey, this.authKeyID, this.serverSalt */);
|
this.log('constructor'/* , this.authKey, this.authKeyID, this.serverSalt */);
|
||||||
|
|
||||||
// Test resend after bad_server_salt
|
// Test resend after bad_server_salt
|
||||||
@ -168,33 +179,12 @@ export default class MTPNetworker {
|
|||||||
// rootScope.offlineConnecting = true */
|
// rootScope.offlineConnecting = true */
|
||||||
// }
|
// }
|
||||||
|
|
||||||
/// #if MTPROTO_HTTP_UPLOAD
|
|
||||||
if(this.transport instanceof HTTP) {
|
|
||||||
/* this.longPollInt = */setInterval(this.checkLongPoll, 10000);
|
|
||||||
this.checkLongPoll();
|
|
||||||
} else {
|
|
||||||
(this.transport as TcpObfuscated).networker = this;
|
|
||||||
}
|
|
||||||
/// #elif MTPROTO_HTTP
|
|
||||||
//if(this.transport instanceof HTTP) {
|
|
||||||
/* this.longPollInt = */setInterval(this.checkLongPoll, 10000);
|
|
||||||
this.checkLongPoll();
|
|
||||||
/// #else
|
|
||||||
//} else {
|
|
||||||
(this.transport as TcpObfuscated).networker = this;
|
|
||||||
//}
|
|
||||||
/// #endif
|
|
||||||
|
|
||||||
// * handle outcoming dead socket, server will close the connection
|
// * handle outcoming dead socket, server will close the connection
|
||||||
// if((this.transport as TcpObfuscated).networker) {
|
// if((this.transport as TcpObfuscated).networker) {
|
||||||
// this.disconnectDelay = /* (this.transport as TcpObfuscated).retryTimeout */75;
|
// this.disconnectDelay = /* (this.transport as TcpObfuscated).retryTimeout */75;
|
||||||
// //setInterval(this.sendPingDelayDisconnect, (this.disconnectDelay - 5) * 1000);
|
// //setInterval(this.sendPingDelayDisconnect, (this.disconnectDelay - 5) * 1000);
|
||||||
// this.sendPingDelayDisconnect();
|
// this.sendPingDelayDisconnect();
|
||||||
// }
|
// }
|
||||||
|
|
||||||
if((this.transport as TcpObfuscated).connected) {
|
|
||||||
this.setConnectionStatus(ConnectionStatus.Connected);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private updateSession() {
|
private updateSession() {
|
||||||
@ -232,9 +222,9 @@ export default class MTPNetworker {
|
|||||||
sentMessage.msg_id = timeManager.generateId();
|
sentMessage.msg_id = timeManager.generateId();
|
||||||
sentMessage.seq_no = this.generateSeqNo(sentMessage.notContentRelated || sentMessage.container);
|
sentMessage.seq_no = this.generateSeqNo(sentMessage.notContentRelated || sentMessage.container);
|
||||||
|
|
||||||
/* if(DEBUG) {
|
if(this.debug) {
|
||||||
this.log('updateSentMessage', sentMessage.msg_id, sentMessageId);
|
this.log(`updateSentMessage, old=${sentMessageId}, new=${sentMessage.msg_id}`);
|
||||||
} */
|
}
|
||||||
|
|
||||||
this.sentMessages[sentMessage.msg_id] = sentMessage;
|
this.sentMessages[sentMessage.msg_id] = sentMessage;
|
||||||
delete this.sentMessages[sentMessageId];
|
delete this.sentMessages[sentMessageId];
|
||||||
@ -372,20 +362,75 @@ export default class MTPNetworker {
|
|||||||
return this.pushMessage(message, options);
|
return this.pushMessage(message, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public changeTransport(transport?: MTTransport) {
|
||||||
|
const oldTransport = this.transport;
|
||||||
|
if(oldTransport) {
|
||||||
|
oldTransport.destroy();
|
||||||
|
|
||||||
|
DcConfigurator.removeTransport(dcConfigurator.chosenServers, this.transport);
|
||||||
|
|
||||||
|
if(this.nextReqTimeout) {
|
||||||
|
clearTimeout(this.nextReqTimeout);
|
||||||
|
this.nextReqTimeout = 0;
|
||||||
|
this.nextReq = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// #if MTPROTO_HAS_HTTP
|
||||||
|
if(this.longPollInterval !== undefined) {
|
||||||
|
clearInterval(this.longPollInterval);
|
||||||
|
this.longPollInterval = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(this.checkConnectionTimeout !== undefined) {
|
||||||
|
clearTimeout(this.checkConnectionTimeout);
|
||||||
|
this.checkConnectionTimeout = undefined;
|
||||||
|
}
|
||||||
|
/// #endif
|
||||||
|
}
|
||||||
|
|
||||||
|
this.transport = transport;
|
||||||
|
if(!transport) {
|
||||||
|
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
|
||||||
|
|
||||||
|
if(transport.connected && (transport as TcpObfuscated).connection) {
|
||||||
|
this.setConnectionStatus(ConnectionStatus.Connected);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.resend();
|
||||||
|
}
|
||||||
|
|
||||||
public destroy() {
|
public destroy() {
|
||||||
//assumeType<TcpObfuscated>(this.transport);
|
this.changeTransport();
|
||||||
(this.transport as TcpObfuscated).destroy();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public forceReconnectTimeout() {
|
public forceReconnectTimeout() {
|
||||||
if((this.transport as TcpObfuscated).reconnect) {
|
if((this.transport as TcpObfuscated).reconnect) {
|
||||||
(this.transport as TcpObfuscated).reconnect();
|
(this.transport as TcpObfuscated).reconnect();
|
||||||
|
} else {
|
||||||
|
this.resend();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public forceReconnect() {
|
public forceReconnect() {
|
||||||
if((this.transport as TcpObfuscated).forceReconnect) {
|
if((this.transport as TcpObfuscated).forceReconnect) {
|
||||||
(this.transport as TcpObfuscated).forceReconnect();
|
(this.transport as TcpObfuscated).forceReconnect();
|
||||||
|
} else {
|
||||||
|
this.checkConnection('force reconnect');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -496,13 +541,14 @@ export default class MTPNetworker {
|
|||||||
// });
|
// });
|
||||||
// };
|
// };
|
||||||
|
|
||||||
/// #if MTPROTO_HTTP || MTPROTO_HTTP_UPLOAD
|
/// #if MTPROTO_HAS_HTTP
|
||||||
private checkLongPoll = () => {
|
private checkLongPoll = () => {
|
||||||
const isClean = this.cleanupSent();
|
const isClean = this.cleanupSent();
|
||||||
//this.log.error('Check lp', this.longPollPending, this.dcId, isClean, this);
|
//this.log.error('Check lp', this.longPollPending, this.dcId, isClean, this);
|
||||||
if((this.longPollPending && Date.now() < this.longPollPending) ||
|
if((this.longPollPending && Date.now() < this.longPollPending) ||
|
||||||
this.offline ||
|
this.offline ||
|
||||||
this.isStopped()) {
|
this.isStopped() ||
|
||||||
|
this.isFileNetworker) {
|
||||||
//this.log('No lp this time');
|
//this.log('No lp this time');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -510,7 +556,6 @@ export default class MTPNetworker {
|
|||||||
sessionStorage.get('dc').then((baseDcId) => {
|
sessionStorage.get('dc').then((baseDcId) => {
|
||||||
if(isClean && (
|
if(isClean && (
|
||||||
baseDcId !== this.dcId ||
|
baseDcId !== this.dcId ||
|
||||||
this.isFileNetworker ||
|
|
||||||
(this.sleepAfter && Date.now() > this.sleepAfter)
|
(this.sleepAfter && Date.now() > this.sleepAfter)
|
||||||
)) {
|
)) {
|
||||||
//console.warn(dT(), 'Send long-poll for DC is delayed', this.dcId, this.sleepAfter);
|
//console.warn(dT(), 'Send long-poll for DC is delayed', this.dcId, this.sleepAfter);
|
||||||
@ -522,10 +567,12 @@ export default class MTPNetworker {
|
|||||||
};
|
};
|
||||||
|
|
||||||
private sendLongPoll() {
|
private sendLongPoll() {
|
||||||
|
if(this.sendingLongPoll) return;
|
||||||
|
this.sendingLongPoll = true;
|
||||||
const maxWait = 25000;
|
const maxWait = 25000;
|
||||||
|
|
||||||
this.longPollPending = Date.now() + maxWait;
|
this.longPollPending = Date.now() + maxWait;
|
||||||
//this.log('Set lp', this.longPollPending, tsNow())
|
this.debug && this.log.debug('sendLongPoll', this.longPollPending);
|
||||||
|
|
||||||
this.wrapMtpCall('http_wait', {
|
this.wrapMtpCall('http_wait', {
|
||||||
max_delay: 500,
|
max_delay: 500,
|
||||||
@ -535,19 +582,19 @@ export default class MTPNetworker {
|
|||||||
noResponse: true,
|
noResponse: true,
|
||||||
longPoll: true
|
longPoll: true
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
this.longPollPending = 0;
|
this.longPollPending = undefined;
|
||||||
setTimeout(this.checkLongPoll, 0);
|
setTimeout(this.checkLongPoll, 0);
|
||||||
}, (error: ErrorEvent) => {
|
}, (error: ErrorEvent) => {
|
||||||
this.log('Long-poll failed', error);
|
this.log('Long-poll failed', error);
|
||||||
|
}).finally(() => {
|
||||||
|
this.sendingLongPoll = undefined;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private checkConnection = (event: Event | string) => {
|
private checkConnection = (event: Event | string) => {
|
||||||
/* rootScope.offlineConnecting = true */
|
this.debug && this.log('Check connection', event);
|
||||||
|
|
||||||
this.log('Check connection', event);
|
|
||||||
clearTimeout(this.checkConnectionTimeout);
|
clearTimeout(this.checkConnectionTimeout);
|
||||||
this.checkConnectionTimeout = 0;
|
this.checkConnectionTimeout = undefined;
|
||||||
|
|
||||||
const serializer = new TLSerialization({mtproto: true});
|
const serializer = new TLSerialization({mtproto: true});
|
||||||
const pingId = randomLong();
|
const pingId = randomLong();
|
||||||
@ -562,30 +609,24 @@ export default class MTPNetworker {
|
|||||||
body: serializer.getBytes(true)
|
body: serializer.getBytes(true)
|
||||||
};
|
};
|
||||||
|
|
||||||
this.sendEncryptedRequest(pingMessage).then((result) => {
|
if(this.offline) {
|
||||||
/* delete rootScope.offlineConnecting */
|
this.setConnectionStatus(ConnectionStatus.Connecting);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.sendEncryptedRequest(pingMessage).then(() => {
|
||||||
this.toggleOffline(false);
|
this.toggleOffline(false);
|
||||||
}, () => {
|
}, () => {
|
||||||
this.log('Delay ', this.checkConnectionPeriod * 1000);
|
this.debug && this.log('Delay ', this.checkConnectionPeriod * 1000);
|
||||||
this.checkConnectionTimeout = setTimeout(this.checkConnection, this.checkConnectionPeriod * 1000 | 0);
|
this.checkConnectionTimeout = ctx.setTimeout(this.checkConnection, this.checkConnectionPeriod * 1000 | 0);
|
||||||
this.checkConnectionPeriod = Math.min(60, this.checkConnectionPeriod * 1.5);
|
this.checkConnectionPeriod = Math.min(60, this.checkConnectionPeriod * 1.5);
|
||||||
/* setTimeout(function() {
|
|
||||||
delete rootScope.offlineConnecting
|
|
||||||
}, 1000); */
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
private toggleOffline(enabled: boolean) {
|
private toggleOffline(offline: boolean) {
|
||||||
// this.log('toggle ', enabled, this.dcId, this.iii)
|
if(this.offline !== offline) {
|
||||||
if(this.offline !== undefined && this.offline === enabled) {
|
this.offline = offline;
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.offline = enabled;
|
if(offline) {
|
||||||
/* rootScope.offline = enabled;
|
|
||||||
rootScope.offlineConnecting = false; */
|
|
||||||
|
|
||||||
if(this.offline) {
|
|
||||||
clearTimeout(this.nextReqTimeout);
|
clearTimeout(this.nextReqTimeout);
|
||||||
this.nextReqTimeout = 0;
|
this.nextReqTimeout = 0;
|
||||||
this.nextReq = 0;
|
this.nextReq = 0;
|
||||||
@ -594,7 +635,10 @@ export default class MTPNetworker {
|
|||||||
this.checkConnectionPeriod = 0;
|
this.checkConnectionPeriod = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.checkConnectionTimeout = setTimeout(this.checkConnection, this.checkConnectionPeriod * 1000 | 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);
|
this.checkConnectionPeriod = Math.min(30, (1 + this.checkConnectionPeriod) * 1.5);
|
||||||
|
|
||||||
/// #if !MTPROTO_WORKER
|
/// #if !MTPROTO_WORKER
|
||||||
@ -602,6 +646,7 @@ export default class MTPNetworker {
|
|||||||
document.body.addEventListener('focus', this.checkConnection, false);
|
document.body.addEventListener('focus', this.checkConnection, false);
|
||||||
/// #endif
|
/// #endif
|
||||||
} else {
|
} else {
|
||||||
|
this.setConnectionStatus(ConnectionStatus.Connected);
|
||||||
this.checkLongPoll();
|
this.checkLongPoll();
|
||||||
|
|
||||||
this.scheduleRequest();
|
this.scheduleRequest();
|
||||||
@ -612,58 +657,48 @@ export default class MTPNetworker {
|
|||||||
/// #endif
|
/// #endif
|
||||||
|
|
||||||
clearTimeout(this.checkConnectionTimeout);
|
clearTimeout(this.checkConnectionTimeout);
|
||||||
this.checkConnectionTimeout = 0;
|
this.checkConnectionTimeout = undefined;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.setConnectionStatus(offline ? ConnectionStatus.Closed : ConnectionStatus.Connected, offline ? this.checkConnectionRetryAt : undefined);
|
||||||
}
|
}
|
||||||
|
|
||||||
private handleSentEncryptedRequestHTTP(promise: ReturnType<MTPNetworker['sendEncryptedRequest']>, message: MTMessage, noResponseMsgs: string[]) {
|
private handleSentEncryptedRequestHTTP(promise: ReturnType<MTPNetworker['sendEncryptedRequest']>, message: MTMessage, noResponseMsgs: string[]) {
|
||||||
promise
|
// let timeout = setTimeout(() => {
|
||||||
.then((result) => {
|
// this.log.error('handleSentEncryptedRequestHTTP timeout', promise, message, noResponseMsgs);
|
||||||
|
// }, 5e3);
|
||||||
|
|
||||||
|
promise.then((result) => {
|
||||||
this.toggleOffline(false);
|
this.toggleOffline(false);
|
||||||
// this.log('parse for', message)
|
// this.log('parse for', message);
|
||||||
this.parseResponse(result).then((response) => {
|
return this.parseResponse(result).then((response) => {
|
||||||
if(Modes.debug) {
|
this.debug && this.log.debug('Server response', response);
|
||||||
this.log.debug('Server response', response);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.processMessage(response.response, response.messageId, response.sessionId);
|
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.checkLongPoll();
|
||||||
|
|
||||||
this.checkConnectionPeriod = Math.max(1.1, Math.sqrt(this.checkConnectionPeriod));
|
this.checkConnectionPeriod = Math.max(1.1, Math.sqrt(this.checkConnectionPeriod));
|
||||||
|
|
||||||
|
return true;
|
||||||
});
|
});
|
||||||
}, (error) => {
|
}, (error) => {
|
||||||
this.log.error('Encrypted request failed', error, message);
|
this.log.error('Encrypted request failed', error, message);
|
||||||
|
|
||||||
if(message.container) {
|
this.pushResend(message.msg_id);
|
||||||
message.inner.forEach((msgId: string) => {
|
this.toggleOffline(true);
|
||||||
this.pendingMessages[msgId] = 0;
|
|
||||||
});
|
|
||||||
|
|
||||||
delete this.sentMessages[message.msg_id];
|
|
||||||
} else {
|
|
||||||
this.pendingMessages[message.msg_id] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}).then((shouldResolve) => {
|
||||||
|
// clearTimeout(timeout);
|
||||||
noResponseMsgs.forEach((msgId) => {
|
noResponseMsgs.forEach((msgId) => {
|
||||||
if(this.sentMessages[msgId]) {
|
if(this.sentMessages[msgId]) {
|
||||||
const deferred = this.sentMessages[msgId].deferred;
|
const deferred = this.sentMessages[msgId].deferred;
|
||||||
delete this.sentMessages[msgId];
|
delete this.sentMessages[msgId];
|
||||||
delete this.pendingMessages[msgId];
|
delete this.pendingMessages[msgId];
|
||||||
deferred.reject();
|
shouldResolve ? deferred.resolve() : deferred.reject();
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
|
|
||||||
this.toggleOffline(true);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
/// #endif
|
/// #endif
|
||||||
@ -732,7 +767,7 @@ export default class MTPNetworker {
|
|||||||
|
|
||||||
public setDrainTimeout() {
|
public setDrainTimeout() {
|
||||||
if(!this.activeRequests && this.onDrain && this.onDrainTimeout === undefined) {
|
if(!this.activeRequests && this.onDrain && this.onDrainTimeout === undefined) {
|
||||||
this.onDrainTimeout = self.setTimeout(() => {
|
this.onDrainTimeout = ctx.setTimeout(() => {
|
||||||
this.onDrainTimeout = undefined;
|
this.onDrainTimeout = undefined;
|
||||||
this.log('drain');
|
this.log('drain');
|
||||||
this.onDrain();
|
this.onDrain();
|
||||||
@ -890,7 +925,7 @@ export default class MTPNetworker {
|
|||||||
//const currentTime = Date.now();
|
//const currentTime = Date.now();
|
||||||
let messagesByteLen = 0;
|
let messagesByteLen = 0;
|
||||||
|
|
||||||
/// #if MTPROTO_HTTP || MTPROTO_HTTP_UPLOAD
|
/// #if MTPROTO_HAS_HTTP
|
||||||
let hasApiCall = false;
|
let hasApiCall = false;
|
||||||
let hasHttpWait = false;
|
let hasHttpWait = false;
|
||||||
/// #endif
|
/// #endif
|
||||||
@ -923,7 +958,7 @@ export default class MTPNetworker {
|
|||||||
messages.push(message);
|
messages.push(message);
|
||||||
messagesByteLen += messageByteLength;
|
messagesByteLen += messageByteLength;
|
||||||
|
|
||||||
/// #if MTPROTO_HTTP || MTPROTO_HTTP_UPLOAD
|
/// #if MTPROTO_HAS_HTTP
|
||||||
if(message.isAPI) {
|
if(message.isAPI) {
|
||||||
hasApiCall = true;
|
hasApiCall = true;
|
||||||
} else if(message.longPoll) {
|
} else if(message.longPoll) {
|
||||||
@ -940,10 +975,10 @@ export default class MTPNetworker {
|
|||||||
//}
|
//}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// #if MTPROTO_HTTP_UPLOAD
|
/// #if MTPROTO_HAS_HTTP
|
||||||
|
/// #if MTPROTO_HAS_WS
|
||||||
if(this.transport instanceof HTTP)
|
if(this.transport instanceof HTTP)
|
||||||
/// #endif
|
/// #endif
|
||||||
/// #if MTPROTO_HTTP || MTPROTO_HTTP_UPLOAD
|
|
||||||
if(hasApiCall && !hasHttpWait) {
|
if(hasApiCall && !hasHttpWait) {
|
||||||
const serializer = new TLSerialization({mtproto: true});
|
const serializer = new TLSerialization({mtproto: true});
|
||||||
serializer.storeMethod('http_wait', {
|
serializer.storeMethod('http_wait', {
|
||||||
@ -965,7 +1000,7 @@ export default class MTPNetworker {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// #if MTPROTO_HTTP_UPLOAD || MTPROTO_HTTP
|
/// #if MTPROTO_HAS_HTTP
|
||||||
const noResponseMsgs: Array<string> = messages.filter(message => message.noResponse).map(message => message.msg_id);
|
const noResponseMsgs: Array<string> = messages.filter(message => message.noResponse).map(message => message.msg_id);
|
||||||
/// #endif
|
/// #endif
|
||||||
|
|
||||||
@ -982,18 +1017,18 @@ export default class MTPNetworker {
|
|||||||
|
|
||||||
const promise = this.sendEncryptedRequest(outMessage);
|
const promise = this.sendEncryptedRequest(outMessage);
|
||||||
|
|
||||||
/// #if MTPROTO_HTTP_UPLOAD
|
/// #if MTPROTO_HAS_HTTP
|
||||||
if(!(this.transport instanceof HTTP)) {
|
/// #if MTPROTO_HAS_WS
|
||||||
//if(noResponseMsgs.length) this.log.error('noResponseMsgs length!', noResponseMsgs);
|
if(this.transport instanceof HTTP)
|
||||||
this.cleanupSent(); // ! WARNING
|
/// #endif
|
||||||
} else {
|
|
||||||
this.handleSentEncryptedRequestHTTP(promise, outMessage, noResponseMsgs);
|
this.handleSentEncryptedRequestHTTP(promise, outMessage, noResponseMsgs);
|
||||||
}
|
/// #endif
|
||||||
/// #elif !MTPROTO_HTTP
|
|
||||||
|
/// #if MTPROTO_HAS_WS
|
||||||
|
/// #if MTPROTO_HAS_HTTP
|
||||||
|
if(!(this.transport instanceof HTTP))
|
||||||
|
/// #endif
|
||||||
this.cleanupSent(); // ! WARNING
|
this.cleanupSent(); // ! WARNING
|
||||||
/// #else
|
|
||||||
this.handleSentEncryptedRequestHTTP(promise, outMessage, noResponseMsgs);
|
|
||||||
//}
|
|
||||||
/// #endif
|
/// #endif
|
||||||
|
|
||||||
if(lengthOverflow) {
|
if(lengthOverflow) {
|
||||||
@ -1154,24 +1189,30 @@ export default class MTPNetworker {
|
|||||||
|
|
||||||
private sendEncryptedRequest(message: MTMessage) {
|
private sendEncryptedRequest(message: MTMessage) {
|
||||||
return this.getEncryptedOutput(message).then(requestData => {
|
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<Uint8Array> = this.transport.send(requestData) as any;
|
const promise: Promise<Uint8Array> = this.transport.send(requestData) as any;
|
||||||
/// #if !MTPROTO_HTTP && !MTPROTO_HTTP_UPLOAD
|
// this.debug && this.log.debug('sendEncryptedRequest: launched message into space:', message, promise);
|
||||||
|
|
||||||
|
/// #if !MTPROTO_HAS_HTTP
|
||||||
return promise;
|
return promise;
|
||||||
/// #else
|
/// #else
|
||||||
|
|
||||||
|
/// #if MTPROTO_HAS_WS
|
||||||
if(!(this.transport instanceof HTTP)) return promise;
|
if(!(this.transport instanceof HTTP)) return promise;
|
||||||
|
/// #endif
|
||||||
|
|
||||||
const baseError = {
|
const baseError = {
|
||||||
code: 406,
|
code: 406,
|
||||||
type: 'NETWORK_BAD_RESPONSE',
|
type: 'NETWORK_BAD_RESPONSE',
|
||||||
transport: this.transport
|
transport: this.transport
|
||||||
};
|
};
|
||||||
|
|
||||||
return promise.then((result) => {
|
return promise.then((result) => {
|
||||||
if(!result || !result.byteLength) {
|
if(!result?.byteLength) {
|
||||||
return Promise.reject(baseError);
|
throw baseError;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// this.debug && this.log.debug('sendEncryptedRequest: got response for:', message, [message.msg_id].concat(message.inner || []));
|
||||||
return result;
|
return result;
|
||||||
}, (error) => {
|
}, (error) => {
|
||||||
if(!error.message && !error.type) {
|
if(!error.message && !error.type) {
|
||||||
@ -1180,7 +1221,8 @@ export default class MTPNetworker {
|
|||||||
originalError: error
|
originalError: error
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return Promise.reject(error);
|
|
||||||
|
throw error;
|
||||||
});
|
});
|
||||||
/// #endif
|
/// #endif
|
||||||
});
|
});
|
||||||
@ -1322,13 +1364,18 @@ export default class MTPNetworker {
|
|||||||
return;
|
return;
|
||||||
} */
|
} */
|
||||||
|
|
||||||
/// #if MTPROTO_HTTP || MTPROTO_HTTP_UPLOAD
|
/// #if MTPROTO_HAS_HTTP
|
||||||
if(!(this.transport instanceof HTTP)) {
|
/// #if MTPROTO_HAS_WS
|
||||||
this.performScheduledRequest();
|
if(this.transport instanceof HTTP) {
|
||||||
return;
|
/// #endif
|
||||||
} else if(this.offline) {
|
if(this.offline) {
|
||||||
this.checkConnection('forced schedule');
|
this.checkConnection('forced schedule');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
delay ||= 0; // set zero timeout to pack other messages too
|
||||||
|
/// #if MTPROTO_HAS_WS
|
||||||
|
}
|
||||||
|
/// #endif
|
||||||
/// #endif
|
/// #endif
|
||||||
|
|
||||||
const nextReq = Date.now() + (delay || 0);
|
const nextReq = Date.now() + (delay || 0);
|
||||||
@ -1354,23 +1401,22 @@ export default class MTPNetworker {
|
|||||||
this.nextReqTimeout = 0;
|
this.nextReqTimeout = 0;
|
||||||
this.nextReq = 0;
|
this.nextReq = 0;
|
||||||
|
|
||||||
/// #if MTPROTO_HTTP || MTPROTO_HTTP_UPLOAD
|
/// #if MTPROTO_HAS_HTTP
|
||||||
|
/// #if MTPROTO_HAS_WS
|
||||||
|
if(this.transport instanceof HTTP)
|
||||||
|
/// #endif
|
||||||
if(this.offline) {
|
if(this.offline) {
|
||||||
//this.log('Cancel scheduled');
|
//this.log('Cancel scheduled');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
/// #else
|
/// #endif
|
||||||
/* if(!this.isOnline) {
|
|
||||||
return;
|
|
||||||
} */
|
|
||||||
|
|
||||||
this.performScheduledRequest();
|
this.performScheduledRequest();
|
||||||
/// #endif
|
|
||||||
};
|
};
|
||||||
|
|
||||||
this.nextReq = nextReq;
|
this.nextReq = nextReq;
|
||||||
|
|
||||||
if(delay) {
|
if(delay !== undefined) {
|
||||||
this.nextReqTimeout = self.setTimeout(cb, delay);
|
this.nextReqTimeout = self.setTimeout(cb, delay);
|
||||||
} else {
|
} else {
|
||||||
cb();
|
cb();
|
||||||
@ -1381,11 +1427,16 @@ export default class MTPNetworker {
|
|||||||
// this.log('ack message', msgID)
|
// this.log('ack message', msgID)
|
||||||
this.pendingAcks.push(msgId);
|
this.pendingAcks.push(msgId);
|
||||||
|
|
||||||
/// #if MTPROTO_HTTP || MTPROTO_HTTP_UPLOAD
|
let delay: number;
|
||||||
this.scheduleRequest(30000);
|
|
||||||
/// #else
|
/// #if MTPROTO_HAS_HTTP
|
||||||
this.scheduleRequest();
|
/// #if MTPROTO_HAS_WS
|
||||||
|
if(this.transport instanceof HTTP)
|
||||||
/// #endif
|
/// #endif
|
||||||
|
delay = 30000;
|
||||||
|
/// #endif
|
||||||
|
|
||||||
|
this.scheduleRequest(delay);
|
||||||
}
|
}
|
||||||
|
|
||||||
private reqResendMessage(msgId: MTLong) {
|
private reqResendMessage(msgId: MTLong) {
|
||||||
@ -1680,8 +1731,7 @@ export default class MTPNetworker {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'pong': { // * https://core.telegram.org/mtproto/service_messages#ping-messages-pingpong - These messages doesn't require acknowledgments
|
case 'pong': { // * https://core.telegram.org/mtproto/service_messages#ping-messages-pingpong - These messages don't require acknowledgments
|
||||||
if((this.transport as TcpObfuscated).networker) {
|
|
||||||
const sentMessageId = message.msg_id;
|
const sentMessageId = message.msg_id;
|
||||||
const sentMessage = this.sentMessages[sentMessageId];
|
const sentMessage = this.sentMessages[sentMessageId];
|
||||||
|
|
||||||
@ -1689,7 +1739,6 @@ export default class MTPNetworker {
|
|||||||
sentMessage.deferred.resolve(message);
|
sentMessage.deferred.resolve(message);
|
||||||
delete this.sentMessages[sentMessageId];
|
delete this.sentMessages[sentMessageId];
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -12,9 +12,9 @@
|
|||||||
import type { ConnectionStatusChange } from "./connectionStatus";
|
import type { ConnectionStatusChange } from "./connectionStatus";
|
||||||
import MTPNetworker from "./networker";
|
import MTPNetworker from "./networker";
|
||||||
import { InvokeApiOptions } from "../../types";
|
import { InvokeApiOptions } from "../../types";
|
||||||
import MTTransport from "./transports/transport";
|
|
||||||
import App from "../../config/app";
|
import App from "../../config/app";
|
||||||
import { MOUNT_CLASS_TO } from "../../config/debug";
|
import { MOUNT_CLASS_TO } from "../../config/debug";
|
||||||
|
import { indexOfAndSplice } from "../../helpers/array";
|
||||||
|
|
||||||
export class NetworkerFactory {
|
export class NetworkerFactory {
|
||||||
private networkers: MTPNetworker[] = [];
|
private networkers: MTPNetworker[] = [];
|
||||||
@ -25,19 +25,16 @@ export class NetworkerFactory {
|
|||||||
public userAgent = navigator.userAgent;
|
public userAgent = navigator.userAgent;
|
||||||
|
|
||||||
public removeNetworker(networker: MTPNetworker) {
|
public removeNetworker(networker: MTPNetworker) {
|
||||||
const idx = this.networkers.indexOf(networker);
|
indexOfAndSplice(this.networkers, networker);
|
||||||
if(idx !== -1) {
|
|
||||||
this.networkers.splice(idx, 1);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public setUpdatesProcessor(callback: (obj: any) => void) {
|
public setUpdatesProcessor(callback: (obj: any) => void) {
|
||||||
this.updatesProcessor = callback;
|
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);
|
//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);
|
this.networkers.push(networker);
|
||||||
return networker;
|
return networker;
|
||||||
}
|
}
|
||||||
|
113
src/lib/mtproto/transports/controller.ts
Normal file
113
src/lib/mtproto/transports/controller.ts
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
/*
|
||||||
|
* 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 dcConfigurator, { TransportType } 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;
|
||||||
|
|
||||||
|
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 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<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(() => 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const transportController = new MTTransportController();
|
||||||
|
export default transportController;
|
@ -4,28 +4,127 @@
|
|||||||
* https://github.com/morethanwords/tweb/blob/master/LICENSE
|
* https://github.com/morethanwords/tweb/blob/master/LICENSE
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { pause } from '../../../helpers/schedulers/pause';
|
||||||
|
import { DcId } from '../../../types';
|
||||||
|
import { logger, LogTypes } from '../../logger';
|
||||||
|
import type MTPNetworker from '../networker';
|
||||||
import MTTransport from './transport';
|
import MTTransport from './transport';
|
||||||
|
import Modes from '../../../config/modes';
|
||||||
|
|
||||||
|
/// #if MTPROTO_AUTO
|
||||||
|
import transportController from './controller';
|
||||||
|
/// #endif
|
||||||
|
|
||||||
export default class HTTP implements MTTransport {
|
export default class HTTP implements MTTransport {
|
||||||
constructor(protected dcId: number, protected url: string) {
|
public networker: MTPNetworker;
|
||||||
|
private log: ReturnType<typeof logger>;
|
||||||
|
|
||||||
|
private pending: Array<{
|
||||||
|
resolve: (body: Uint8Array) => void,
|
||||||
|
reject: any,
|
||||||
|
body: Uint8Array
|
||||||
|
}> = [];
|
||||||
|
private releasing: boolean;
|
||||||
|
|
||||||
|
public connected: boolean;
|
||||||
|
private destroyed: boolean;
|
||||||
|
private debug: boolean;
|
||||||
|
|
||||||
|
constructor(protected dcId: DcId, protected url: string, logSuffix: string) {
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
public send(data: Uint8Array) {
|
public _send(body: Uint8Array, mode?: RequestMode) {
|
||||||
return fetch(this.url, {method: 'POST', body: data}).then(response => {
|
this.debug && this.log.debug('-> body length to send:', body.length);
|
||||||
//console.log('http response', response/* , response.arrayBuffer() */);
|
|
||||||
|
|
||||||
if(response.status !== 200) {
|
return fetch(this.url, {method: 'POST', body, mode}).then(response => {
|
||||||
|
if(response.status !== 200 && !mode) {
|
||||||
response.arrayBuffer().then(buffer => {
|
response.arrayBuffer().then(buffer => {
|
||||||
console.log('not 200',
|
this.log.error('not 200',
|
||||||
new TextDecoder("utf-8").decode(new Uint8Array(buffer)));
|
new TextDecoder("utf-8").decode(new Uint8Array(buffer)));
|
||||||
})
|
});
|
||||||
|
|
||||||
throw response;
|
throw response;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.setConnected(true);
|
||||||
|
|
||||||
|
// * test resending by dropping random request
|
||||||
|
// if(Math.random() > .5) {
|
||||||
|
// throw 'asd';
|
||||||
|
// }
|
||||||
|
|
||||||
return response.arrayBuffer().then(buffer => {
|
return response.arrayBuffer().then(buffer => {
|
||||||
return new Uint8Array(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);
|
||||||
|
} else {
|
||||||
|
const promise = new Promise<typeof body>((resolve, reject) => {
|
||||||
|
this.pending.push({resolve, reject, body});
|
||||||
|
});
|
||||||
|
|
||||||
|
this.releasePending();
|
||||||
|
|
||||||
|
return promise;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async releasePending() {
|
||||||
|
if(this.releasing) return;
|
||||||
|
|
||||||
|
this.releasing = true;
|
||||||
|
// this.log('-> messages to send:', this.pending.length);
|
||||||
|
for(let i = 0; i < this.pending.length; ++i) {
|
||||||
|
const pending = this.pending[i];
|
||||||
|
const {body, resolve} = pending;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const result = await this._send(body);
|
||||||
|
resolve(result);
|
||||||
|
this.pending.splice(i, 1);
|
||||||
|
} catch(err) {
|
||||||
|
this.log.error('Send plain request error:', err);
|
||||||
|
await pause(5000);
|
||||||
|
}
|
||||||
|
|
||||||
|
--i;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.releasing = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,10 @@ import MTTransport, { MTConnection, MTConnectionConstructable } from "./transpor
|
|||||||
import intermediatePacketCodec from './intermediate';
|
import intermediatePacketCodec from './intermediate';
|
||||||
import { ConnectionStatus } from "../connectionStatus";
|
import { ConnectionStatus } from "../connectionStatus";
|
||||||
|
|
||||||
|
/// #if MTPROTO_AUTO
|
||||||
|
import transportController from "./controller";
|
||||||
|
/// #endif
|
||||||
|
|
||||||
export default class TcpObfuscated implements MTTransport {
|
export default class TcpObfuscated implements MTTransport {
|
||||||
private codec = intermediatePacketCodec;
|
private codec = intermediatePacketCodec;
|
||||||
private obfuscation = new Obfuscation();
|
private obfuscation = new Obfuscation();
|
||||||
@ -29,7 +33,7 @@ export default class TcpObfuscated implements MTTransport {
|
|||||||
private log: ReturnType<typeof logger>;
|
private log: ReturnType<typeof logger>;
|
||||||
public connected = false;
|
public connected = false;
|
||||||
private lastCloseTime: number;
|
private lastCloseTime: number;
|
||||||
private connection: MTConnection;
|
public connection: MTConnection;
|
||||||
|
|
||||||
private autoReconnect = true;
|
private autoReconnect = true;
|
||||||
private reconnectTimeout: number;
|
private reconnectTimeout: number;
|
||||||
@ -53,6 +57,10 @@ export default class TcpObfuscated implements MTTransport {
|
|||||||
private onOpen = () => {
|
private onOpen = () => {
|
||||||
this.connected = true;
|
this.connected = true;
|
||||||
|
|
||||||
|
/// #if MTPROTO_AUTO
|
||||||
|
transportController.setTransportOpened('websocket');
|
||||||
|
/// #endif
|
||||||
|
|
||||||
const initPayload = this.obfuscation.init(this.codec);
|
const initPayload = this.obfuscation.init(this.codec);
|
||||||
|
|
||||||
this.connection.send(initPayload);
|
this.connection.send(initPayload);
|
||||||
@ -136,6 +144,12 @@ export default class TcpObfuscated implements MTTransport {
|
|||||||
};
|
};
|
||||||
|
|
||||||
public clear() {
|
public clear() {
|
||||||
|
/// #if MTPROTO_AUTO
|
||||||
|
if(this.connected) {
|
||||||
|
transportController.setTransportClosed('websocket');
|
||||||
|
}
|
||||||
|
/// #endif
|
||||||
|
|
||||||
this.connected = false;
|
this.connected = false;
|
||||||
|
|
||||||
if(this.connection) {
|
if(this.connection) {
|
||||||
@ -183,6 +197,13 @@ export default class TcpObfuscated implements MTTransport {
|
|||||||
public destroy() {
|
public destroy() {
|
||||||
this.setAutoReconnect(false);
|
this.setAutoReconnect(false);
|
||||||
this.close();
|
this.close();
|
||||||
|
|
||||||
|
this.pending.forEach(pending => {
|
||||||
|
if(pending.reject) {
|
||||||
|
pending.reject();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.pending.length = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public close() {
|
public close() {
|
||||||
@ -274,10 +295,6 @@ export default class TcpObfuscated implements MTTransport {
|
|||||||
let length = this.pending.length;
|
let length = this.pending.length;
|
||||||
//for(let i = length - 1; i >= 0; --i) {
|
//for(let i = length - 1; i >= 0; --i) {
|
||||||
for(let i = 0; i < length; ++i) {
|
for(let i = 0; i < length; ++i) {
|
||||||
/* if(this.ws.bufferedAmount) {
|
|
||||||
break;
|
|
||||||
} */
|
|
||||||
|
|
||||||
const pending = this.pending[i];
|
const pending = this.pending[i];
|
||||||
const {body, bodySent} = pending;
|
const {body, bodySent} = pending;
|
||||||
let encoded = pending.encoded;
|
let encoded = pending.encoded;
|
||||||
@ -286,25 +303,12 @@ export default class TcpObfuscated implements MTTransport {
|
|||||||
//this.debugPayloads.push({before: body.slice(), after: enc});
|
//this.debugPayloads.push({before: body.slice(), after: enc});
|
||||||
|
|
||||||
this.debug && this.log.debug('-> body length to send:', body.length);
|
this.debug && this.log.debug('-> body length to send:', body.length);
|
||||||
/* if(this.ws.bufferedAmount) {
|
|
||||||
this.log.error('bufferedAmount:', this.ws.bufferedAmount);
|
|
||||||
} */
|
|
||||||
|
|
||||||
/* if(this.ws.readyState !== this.ws.OPEN) {
|
|
||||||
this.log.error('ws is closed?');
|
|
||||||
this.connected = false;
|
|
||||||
break;
|
|
||||||
} */
|
|
||||||
|
|
||||||
if(!encoded) {
|
if(!encoded) {
|
||||||
encoded = pending.encoded = this.encodeBody(body);
|
encoded = pending.encoded = this.encodeBody(body);
|
||||||
}
|
}
|
||||||
|
|
||||||
//this.lol.push(body);
|
|
||||||
//setTimeout(() => {
|
|
||||||
this.connection.send(encoded);
|
this.connection.send(encoded);
|
||||||
//}, 100);
|
|
||||||
//this.dd();
|
|
||||||
|
|
||||||
if(!pending.resolve) { // remove if no response needed
|
if(!pending.resolve) { // remove if no response needed
|
||||||
this.pending.splice(i--, 1);
|
this.pending.splice(i--, 1);
|
||||||
|
@ -5,9 +5,13 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import type EventListenerBase from "../../../helpers/eventListenerBase";
|
import type EventListenerBase from "../../../helpers/eventListenerBase";
|
||||||
|
import type MTPNetworker from "../networker";
|
||||||
|
|
||||||
export default interface MTTransport {
|
export default interface MTTransport {
|
||||||
|
networker: MTPNetworker;
|
||||||
send: (data: Uint8Array) => void;
|
send: (data: Uint8Array) => void;
|
||||||
|
connected: boolean;
|
||||||
|
destroy: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface MTConnection extends EventListenerBase<{
|
export interface MTConnection extends EventListenerBase<{
|
||||||
@ -15,7 +19,7 @@ export interface MTConnection extends EventListenerBase<{
|
|||||||
message: (buffer: ArrayBuffer) => any,
|
message: (buffer: ArrayBuffer) => any,
|
||||||
close: () => void,
|
close: () => void,
|
||||||
}> {
|
}> {
|
||||||
send: (data: Uint8Array) => void;
|
send: MTTransport['send'];
|
||||||
close: () => void;
|
close: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,6 +9,9 @@ import Modes from '../../../config/modes';
|
|||||||
import EventListenerBase from '../../../helpers/eventListenerBase';
|
import EventListenerBase from '../../../helpers/eventListenerBase';
|
||||||
import { MTConnection } from './transport';
|
import { MTConnection } from './transport';
|
||||||
|
|
||||||
|
// let closeSocketBefore = Date.now() + 30e3;
|
||||||
|
// let closeSocketAfter = Date.now() + 10e3;
|
||||||
|
|
||||||
export default class Socket extends EventListenerBase<{
|
export default class Socket extends EventListenerBase<{
|
||||||
open: () => void,
|
open: () => void,
|
||||||
message: (buffer: ArrayBuffer) => any,
|
message: (buffer: ArrayBuffer) => any,
|
||||||
@ -49,6 +52,11 @@ export default class Socket extends EventListenerBase<{
|
|||||||
this.ws.addEventListener('close', this.handleClose);
|
this.ws.addEventListener('close', this.handleClose);
|
||||||
this.ws.addEventListener('error', this.handleError);
|
this.ws.addEventListener('error', this.handleError);
|
||||||
this.ws.addEventListener('message', this.handleMessage);
|
this.ws.addEventListener('message', this.handleMessage);
|
||||||
|
|
||||||
|
// if(Date.now() < closeSocketBefore) {
|
||||||
|
// if(Date.now() >= closeSocketAfter) {
|
||||||
|
// this.ws.close();
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
public close() {
|
public close() {
|
||||||
|
@ -35,6 +35,7 @@ let onFirstMount = () => {
|
|||||||
// setTimeout(() => {
|
// setTimeout(() => {
|
||||||
const promise = import('../lib/appManagers/appDialogsManager');
|
const promise = import('../lib/appManagers/appDialogsManager');
|
||||||
promise.finally(async() => {
|
promise.finally(async() => {
|
||||||
|
document.getElementById('auth-pages').remove();
|
||||||
//alert('pageIm!');
|
//alert('pageIm!');
|
||||||
resolve();
|
resolve();
|
||||||
|
|
||||||
|
@ -84,6 +84,9 @@ let onFirstMount = (): Promise<any> => {
|
|||||||
btnNextI18n.update({key: 'PleaseWait'});
|
btnNextI18n.update({key: 'PleaseWait'});
|
||||||
const preloader = putPreloader(btnNext);
|
const preloader = putPreloader(btnNext);
|
||||||
|
|
||||||
|
passwordInputField.setValueSilently('' + Math.random()); // prevent saving suggestion
|
||||||
|
passwordInputField.setValueSilently(value); // prevent saving suggestion
|
||||||
|
|
||||||
passwordManager.check(value, state).then((response) => {
|
passwordManager.check(value, state).then((response) => {
|
||||||
//console.log('passwordManager response:', response);
|
//console.log('passwordManager response:', response);
|
||||||
|
|
||||||
|
@ -393,6 +393,9 @@ input:focus, button:focus {
|
|||||||
&[type="password"] {
|
&[type="password"] {
|
||||||
font-size: 2.25rem;
|
font-size: 2.25rem;
|
||||||
padding-left: calc(.875rem - var(--border-width));
|
padding-left: calc(.875rem - var(--border-width));
|
||||||
|
line-height: 1;
|
||||||
|
padding-top: 0;
|
||||||
|
padding-bottom: 0;
|
||||||
|
|
||||||
@media (-webkit-min-device-pixel-ratio: 2) {
|
@media (-webkit-min-device-pixel-ratio: 2) {
|
||||||
font-size: 1.75rem;
|
font-size: 1.75rem;
|
||||||
|
@ -555,6 +555,14 @@ input, textarea {
|
|||||||
-webkit-appearance: none;
|
-webkit-appearance: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Possible fix Safari's password autocomplete
|
||||||
|
input::-webkit-contacts-auto-fill-button,
|
||||||
|
input::-webkit-credentials-auto-fill-button {
|
||||||
|
visibility: hidden;
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* input:-webkit-autofill,
|
/* input:-webkit-autofill,
|
||||||
input:-webkit-autofill:hover,
|
input:-webkit-autofill:hover,
|
||||||
input:-webkit-autofill:focus,
|
input:-webkit-autofill:focus,
|
||||||
|
@ -17,11 +17,17 @@ if(devMode) {
|
|||||||
console.log('DEVMODE IS ON!');
|
console.log('DEVMODE IS ON!');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const MTPROTO_HTTP = false;
|
||||||
|
const MTPROTO_AUTO = true;
|
||||||
|
|
||||||
const opts = {
|
const opts = {
|
||||||
MTPROTO_WORKER: true,
|
MTPROTO_WORKER: true,
|
||||||
MTPROTO_SW: false,
|
MTPROTO_SW: false,
|
||||||
MTPROTO_HTTP: false,
|
MTPROTO_HTTP: MTPROTO_HTTP,
|
||||||
MTPROTO_HTTP_UPLOAD: false,
|
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,
|
DEBUG: devMode,
|
||||||
version: 3,
|
version: 3,
|
||||||
'ifdef-verbose': devMode, // add this for verbose output
|
'ifdef-verbose': devMode, // add this for verbose output
|
||||||
|
Loading…
x
Reference in New Issue
Block a user