Network:
Fix stuck requests because of drain race condition Fix releasing transport on drain
This commit is contained in:
parent
becdadfe44
commit
fceec8e8e8
@ -332,7 +332,10 @@ export default class ChatBubbles {
|
|||||||
const timeSpan = div.querySelector('.time');
|
const timeSpan = div.querySelector('.time');
|
||||||
const newDiv = wrapDocument({message});
|
const newDiv = wrapDocument({message});
|
||||||
div.replaceWith(newDiv);
|
div.replaceWith(newDiv);
|
||||||
newDiv.querySelector('.document-size').append(timeSpan);
|
|
||||||
|
if(timeSpan) {
|
||||||
|
newDiv.querySelector('.document-size').append(timeSpan);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(container) {
|
if(container) {
|
||||||
|
@ -13,7 +13,7 @@ export default function getViewportSlice({overflowElement, selector, extraSize}:
|
|||||||
selector: string,
|
selector: string,
|
||||||
extraSize?: number
|
extraSize?: number
|
||||||
}) {
|
}) {
|
||||||
const perf = performance.now();
|
// const perf = performance.now();
|
||||||
const overflowRect = overflowElement.getBoundingClientRect();
|
const overflowRect = overflowElement.getBoundingClientRect();
|
||||||
const elements = Array.from(overflowElement.querySelectorAll<HTMLElement>(selector));
|
const elements = Array.from(overflowElement.querySelectorAll<HTMLElement>(selector));
|
||||||
|
|
||||||
@ -67,7 +67,7 @@ export default function getViewportSlice({overflowElement, selector, extraSize}:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('getViewportSlice time:', performance.now() - perf);
|
// console.log('getViewportSlice time:', performance.now() - perf);
|
||||||
|
|
||||||
return {invisibleTop, visible, invisibleBottom};
|
return {invisibleTop, visible, invisibleBottom};
|
||||||
}
|
}
|
||||||
|
@ -40,6 +40,7 @@ import rootScope from '../rootScope';
|
|||||||
/// #if MTPROTO_AUTO
|
/// #if MTPROTO_AUTO
|
||||||
import transportController from './transports/controller';
|
import transportController from './transports/controller';
|
||||||
import MTTransport from './transports/transport';
|
import MTTransport from './transports/transport';
|
||||||
|
import { pause } from '../../helpers/schedulers/pause';
|
||||||
/// #endif
|
/// #endif
|
||||||
|
|
||||||
/* var networker = apiManager.cachedNetworkers.websocket.upload[2];
|
/* var networker = apiManager.cachedNetworkers.websocket.upload[2];
|
||||||
@ -407,7 +408,7 @@ export class ApiManager {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private changeNetworkerTransport(networker: MTPNetworker, transport: MTTransport) {
|
private changeNetworkerTransport(networker: MTPNetworker, transport?: MTTransport) {
|
||||||
const oldTransport = networker.transport;
|
const oldTransport = networker.transport;
|
||||||
if(oldTransport) {
|
if(oldTransport) {
|
||||||
DcConfigurator.removeTransport(dcConfigurator.chosenServers, oldTransport);
|
DcConfigurator.removeTransport(dcConfigurator.chosenServers, oldTransport);
|
||||||
@ -434,6 +435,7 @@ export class ApiManager {
|
|||||||
this.log('networker drain', networker.dcId);
|
this.log('networker drain', networker.dcId);
|
||||||
|
|
||||||
networker.onDrain = undefined;
|
networker.onDrain = undefined;
|
||||||
|
this.changeNetworkerTransport(networker);
|
||||||
networker.destroy();
|
networker.destroy();
|
||||||
networkerFactory.removeNetworker(networker);
|
networkerFactory.removeNetworker(networker);
|
||||||
DcConfigurator.removeTransport(this.cachedNetworkers, networker);
|
DcConfigurator.removeTransport(this.cachedNetworkers, networker);
|
||||||
@ -475,14 +477,12 @@ export class ApiManager {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const rejectPromise = (error: ApiError) => {
|
const rejectPromise = async(error: ApiError) => {
|
||||||
if(!error) {
|
if(!error) {
|
||||||
error = {type: 'ERROR_EMPTY'};
|
error = {type: 'ERROR_EMPTY'};
|
||||||
} else if(!isObject(error)) {
|
} else if(!isObject(error)) {
|
||||||
error = {message: error};
|
error = {message: error};
|
||||||
}
|
}
|
||||||
|
|
||||||
deferred.reject(error);
|
|
||||||
|
|
||||||
if((error.code === 401 && error.type === 'SESSION_REVOKED') ||
|
if((error.code === 401 && error.type === 'SESSION_REVOKED') ||
|
||||||
(error.code === 406 && error.type === 'AUTH_KEY_DUPLICATED')) {
|
(error.code === 406 && error.type === 'AUTH_KEY_DUPLICATED')) {
|
||||||
@ -490,7 +490,7 @@ export class ApiManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(options.ignoreErrors) {
|
if(options.ignoreErrors) {
|
||||||
return;
|
throw error;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(error.code === 406) {
|
if(error.code === 406) {
|
||||||
@ -512,13 +512,15 @@ export class ApiManager {
|
|||||||
}
|
}
|
||||||
}, 100);
|
}, 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
throw error;
|
||||||
};
|
};
|
||||||
|
|
||||||
let dcId: DcId;
|
let dcId: DcId;
|
||||||
|
|
||||||
let cachedNetworker: MTPNetworker;
|
let cachedNetworker: MTPNetworker;
|
||||||
let stack = (new Error()).stack || 'empty stack';
|
let stack = (new Error()).stack || 'empty stack';
|
||||||
const performRequest = (networker: MTPNetworker) => {
|
const performRequest = (): Promise<any> => {
|
||||||
if(afterMessageId) {
|
if(afterMessageId) {
|
||||||
const after = this.afterMessageTempIds[afterMessageId];
|
const after = this.afterMessageTempIds[afterMessageId];
|
||||||
if(after) {
|
if(after) {
|
||||||
@ -526,7 +528,7 @@ export class ApiManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const promise = (cachedNetworker = networker).wrapApiCall(method, params, options);
|
const promise = cachedNetworker.wrapApiCall(method, params, options);
|
||||||
|
|
||||||
if(prepareTempMessageId) {
|
if(prepareTempMessageId) {
|
||||||
this.afterMessageTempIds[prepareTempMessageId] = {
|
this.afterMessageTempIds[prepareTempMessageId] = {
|
||||||
@ -535,7 +537,7 @@ export class ApiManager {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return promise.then(deferred.resolve, (error: ApiError) => {
|
return promise.catch((error: ApiError) => {
|
||||||
//if(!options.ignoreErrors) {
|
//if(!options.ignoreErrors) {
|
||||||
if(error.type !== 'FILE_REFERENCE_EXPIRED'/* && error.type !== 'MSG_WAIT_FAILED' */) {
|
if(error.type !== 'FILE_REFERENCE_EXPIRED'/* && error.type !== 'MSG_WAIT_FAILED' */) {
|
||||||
this.log.error('Error', error.code, error.type, this.baseDcId, dcId, method, params);
|
this.log.error('Error', error.code, error.type, this.baseDcId, dcId, method, params);
|
||||||
@ -548,7 +550,7 @@ export class ApiManager {
|
|||||||
//this.telegramMeNotify(false);
|
//this.telegramMeNotify(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
rejectPromise(error);
|
throw error;
|
||||||
} else if(error.code === 401 && this.baseDcId && dcId !== this.baseDcId) {
|
} else if(error.code === 401 && this.baseDcId && dcId !== this.baseDcId) {
|
||||||
if(this.cachedExportPromise[dcId] === undefined) {
|
if(this.cachedExportPromise[dcId] === undefined) {
|
||||||
const promise = new Promise((exportResolve, exportReject) => {
|
const promise = new Promise((exportResolve, exportReject) => {
|
||||||
@ -563,10 +565,7 @@ export class ApiManager {
|
|||||||
this.cachedExportPromise[dcId] = promise;
|
this.cachedExportPromise[dcId] = promise;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.cachedExportPromise[dcId].then(() => {
|
return this.cachedExportPromise[dcId].then(() => performRequest());
|
||||||
//(cachedNetworker = networker).wrapApiCall(method, params, options).then(deferred.resolve, rejectPromise);
|
|
||||||
this.invokeApi(method, params, options).then(deferred.resolve, rejectPromise);
|
|
||||||
}, rejectPromise);
|
|
||||||
} else if(error.code === 303) {
|
} else if(error.code === 303) {
|
||||||
const newDcId = +error.type.match(/^(PHONE_MIGRATE_|NETWORK_MIGRATE_|USER_MIGRATE_)(\d+)/)[2] as DcId;
|
const newDcId = +error.type.match(/^(PHONE_MIGRATE_|NETWORK_MIGRATE_|USER_MIGRATE_)(\d+)/)[2] as DcId;
|
||||||
if(newDcId !== dcId) {
|
if(newDcId !== dcId) {
|
||||||
@ -576,70 +575,70 @@ export class ApiManager {
|
|||||||
this.setBaseDcId(newDcId);
|
this.setBaseDcId(newDcId);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.getNetworker(newDcId, options).then((networker) => {
|
return this.invokeApi(method, params, options);
|
||||||
networker.wrapApiCall(method, params, options).then(deferred.resolve, rejectPromise);
|
|
||||||
}, rejectPromise);
|
|
||||||
}
|
}
|
||||||
} else if(error.code === 400 && error.type.indexOf('FILE_MIGRATE') === 0) {
|
} else if(error.code === 400 && error.type.indexOf('FILE_MIGRATE') === 0) {
|
||||||
const newDcId = +error.type.match(/^(FILE_MIGRATE_)(\d+)/)[2] as DcId;
|
const newDcId = +error.type.match(/^(FILE_MIGRATE_)(\d+)/)[2] as DcId;
|
||||||
if(newDcId !== dcId) {
|
if(newDcId !== dcId) {
|
||||||
this.getNetworker(newDcId, options).then((networker) => {
|
options.dcId = newDcId;
|
||||||
networker.wrapApiCall(method, params, options).then(deferred.resolve, rejectPromise);
|
return this.invokeApi(method, params, options);
|
||||||
}, rejectPromise);
|
|
||||||
} else {
|
} else {
|
||||||
rejectPromise(error);
|
throw error;
|
||||||
}
|
}
|
||||||
} else if(error.code === 400 && error.type === 'CONNECTION_NOT_INITED') {
|
} else if(error.code === 400 && error.type === 'CONNECTION_NOT_INITED') {
|
||||||
networkerFactory.unsetConnectionInited();
|
networkerFactory.unsetConnectionInited();
|
||||||
performRequest(cachedNetworker);
|
return performRequest();
|
||||||
} else if(!options.rawError && error.code === 420) {
|
} else if(!options.rawError && error.code === 420) {
|
||||||
const waitTime = +error.type.match(/^FLOOD_WAIT_(\d+)/)[1] || 1;
|
const waitTime = +error.type.match(/^FLOOD_WAIT_(\d+)/)[1] || 1;
|
||||||
|
|
||||||
if(waitTime > (options.floodMaxTimeout !== undefined ? options.floodMaxTimeout : 60) && !options.prepareTempMessageId) {
|
if(waitTime > (options.floodMaxTimeout !== undefined ? options.floodMaxTimeout : 60) && !options.prepareTempMessageId) {
|
||||||
return rejectPromise(error);
|
throw error;
|
||||||
}
|
}
|
||||||
|
|
||||||
setTimeout(() => {
|
return pause(waitTime/* (waitTime + 5) */ * 1000).then(() => performRequest());
|
||||||
performRequest(cachedNetworker);
|
|
||||||
}, waitTime/* (waitTime + 5) */ * 1000); // 03.02.2020
|
|
||||||
} else if(!options.rawError && ['MSG_WAIT_FAILED', 'MSG_WAIT_TIMEOUT'].includes(error.type)) {
|
} else if(!options.rawError && ['MSG_WAIT_FAILED', 'MSG_WAIT_TIMEOUT'].includes(error.type)) {
|
||||||
const after = this.afterMessageTempIds[afterMessageId];
|
const after = this.afterMessageTempIds[afterMessageId];
|
||||||
|
|
||||||
afterMessageId = undefined;
|
afterMessageId = undefined;
|
||||||
delete options.afterMessageId;
|
delete options.afterMessageId;
|
||||||
|
|
||||||
if(after) after.promise.then(() => performRequest(cachedNetworker));
|
if(after) return after.promise.then(() => performRequest());
|
||||||
else performRequest(cachedNetworker);
|
else return performRequest();
|
||||||
} else if(!options.rawError && error.code === 500) {
|
} else if(!options.rawError && error.code === 500) {
|
||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
if(options.stopTime) {
|
if(options.stopTime) {
|
||||||
if(now >= options.stopTime) {
|
if(now >= options.stopTime) {
|
||||||
return rejectPromise(error);
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
options.waitTime = options.waitTime ? Math.min(60, options.waitTime * 1.5) : 1;
|
options.waitTime = options.waitTime ? Math.min(60, options.waitTime * 1.5) : 1;
|
||||||
setTimeout(() => {
|
return pause(options.waitTime * 1000).then(() => performRequest());
|
||||||
performRequest(cachedNetworker);
|
|
||||||
}, options.waitTime * 1000);
|
|
||||||
} else if(error.type === 'UNKNOWN') {
|
} else if(error.type === 'UNKNOWN') {
|
||||||
setTimeout(() => {
|
return pause(1000).then(() => performRequest());
|
||||||
performRequest(cachedNetworker);
|
|
||||||
}, 1000);
|
|
||||||
} else {
|
} else {
|
||||||
rejectPromise(error);
|
throw error;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let p: Promise<MTPNetworker>;
|
||||||
if(dcId = (options.dcId || this.baseDcId)) {
|
if(dcId = (options.dcId || this.baseDcId)) {
|
||||||
this.getNetworker(dcId, options).then(performRequest, rejectPromise);
|
p = this.getNetworker(dcId, options);
|
||||||
} else {
|
} else {
|
||||||
this.getBaseDcId().then(baseDcId => {
|
p = this.getBaseDcId().then((baseDcId) => this.getNetworker(dcId = baseDcId, options));
|
||||||
this.getNetworker(dcId = baseDcId, options).then(performRequest, rejectPromise);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
p.then((networker) => {
|
||||||
|
cachedNetworker = networker;
|
||||||
|
const promise = performRequest();
|
||||||
|
cachedNetworker.attachPromise(deferred, options as MTMessage);
|
||||||
|
return promise;
|
||||||
|
})
|
||||||
|
.then(deferred.resolve)
|
||||||
|
.catch(rejectPromise)
|
||||||
|
.catch(deferred.reject);
|
||||||
|
|
||||||
return deferred;
|
return deferred;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -469,7 +469,7 @@ export default class MTPNetworker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public destroy() {
|
public destroy() {
|
||||||
this.changeTransport();
|
this.log('destroy');
|
||||||
}
|
}
|
||||||
|
|
||||||
public forceReconnectTimeout() {
|
public forceReconnectTimeout() {
|
||||||
@ -812,43 +812,43 @@ export default class MTPNetworker {
|
|||||||
options.messageId = message.msg_id;
|
options.messageId = message.msg_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(promise) {
|
return promise;
|
||||||
const canIncrement = !options.notContentRelated;
|
}
|
||||||
const timeout = setTimeout(() => {
|
|
||||||
if(this.lastResponseTime && (Date.now() - this.lastResponseTime) < this.delays.connectionTimeout) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.log.error('timeout', message);
|
public attachPromise(promise: Promise<any>, message: MTMessage) {
|
||||||
if(this.isOnline) {
|
const canIncrement = true;
|
||||||
this.setConnectionStatus(ConnectionStatus.TimedOut);
|
const timeout = setTimeout(() => {
|
||||||
}
|
if(this.lastResponseTime && (Date.now() - this.lastResponseTime) < this.delays.connectionTimeout) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
/* this.getEncryptedOutput(message).then(bytes => {
|
this.log.error('timeout', message);
|
||||||
this.log.error('timeout encrypted', bytes);
|
if(this.isOnline) {
|
||||||
}); */
|
this.setConnectionStatus(ConnectionStatus.TimedOut);
|
||||||
}, this.delays.connectionTimeout);
|
}
|
||||||
|
|
||||||
promise.catch(noop).finally(() => {
|
/* this.getEncryptedOutput(message).then(bytes => {
|
||||||
clearTimeout(timeout);
|
this.log.error('timeout encrypted', bytes);
|
||||||
this.setConnectionStatus(ConnectionStatus.Connected);
|
}); */
|
||||||
|
}, this.delays.connectionTimeout);
|
||||||
|
|
||||||
|
promise.catch(noop).finally(() => {
|
||||||
|
clearTimeout(timeout);
|
||||||
|
this.setConnectionStatus(ConnectionStatus.Connected);
|
||||||
|
|
||||||
if(canIncrement) {
|
|
||||||
--this.activeRequests;
|
|
||||||
this.setDrainTimeout();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if(canIncrement) {
|
if(canIncrement) {
|
||||||
++this.activeRequests;
|
--this.activeRequests;
|
||||||
if(this.onDrainTimeout !== undefined) {
|
this.setDrainTimeout();
|
||||||
clearTimeout(this.onDrainTimeout);
|
}
|
||||||
this.onDrainTimeout = undefined;
|
});
|
||||||
}
|
|
||||||
|
if(canIncrement) {
|
||||||
|
++this.activeRequests;
|
||||||
|
if(this.onDrainTimeout !== undefined) {
|
||||||
|
clearTimeout(this.onDrainTimeout);
|
||||||
|
this.onDrainTimeout = undefined;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return promise;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public setDrainTimeout() {
|
public setDrainTimeout() {
|
||||||
@ -885,7 +885,7 @@ export default class MTPNetworker {
|
|||||||
this.scheduleRequest();
|
this.scheduleRequest();
|
||||||
}
|
}
|
||||||
|
|
||||||
if((this.transport as TcpObfuscated).connection) {
|
if((this.transport as TcpObfuscated)?.connection) {
|
||||||
this.clearPingDelayDisconnect();
|
this.clearPingDelayDisconnect();
|
||||||
this.sendPingDelayDisconnect();
|
this.sendPingDelayDisconnect();
|
||||||
}
|
}
|
||||||
@ -1288,8 +1288,12 @@ export default class MTPNetworker {
|
|||||||
private async sendEncryptedRequest(message: MTMessage) {
|
private async sendEncryptedRequest(message: MTMessage) {
|
||||||
const requestData = await this.getEncryptedOutput(message);
|
const requestData = await this.getEncryptedOutput(message);
|
||||||
|
|
||||||
|
if(!this.transport) {
|
||||||
|
this.log.error('trying to send something when offline', this.transport, this);
|
||||||
|
}
|
||||||
|
|
||||||
this.debug && this.log.debug('sending:', message, [message.msg_id].concat(message.inner || []), requestData.length);
|
this.debug && this.log.debug('sending:', 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 ? this.transport.send(requestData) as any : Promise.reject({});
|
||||||
// this.debug && this.log.debug('sendEncryptedRequest: launched message into space:', message, promise);
|
// this.debug && this.log.debug('sendEncryptedRequest: launched message into space:', message, promise);
|
||||||
|
|
||||||
/// #if !MTPROTO_HAS_HTTP
|
/// #if !MTPROTO_HAS_HTTP
|
||||||
|
@ -1451,7 +1451,7 @@ hr {
|
|||||||
.tgico-reply_filled:before,
|
.tgico-reply_filled:before,
|
||||||
.tgico-forward_filled:before {
|
.tgico-forward_filled:before {
|
||||||
font-size: 20px !important;
|
font-size: 20px !important;
|
||||||
padding: 0 2px;
|
padding: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ! TEMPORARY
|
// ! TEMPORARY
|
||||||
|
Loading…
x
Reference in New Issue
Block a user