Performance improvements

Supported SSL. Closes #71
Added setZeroTimeout. Now downloading files when tab in background and
improved performance
Multiple file parts download in parallel
TLDeserialization.fetchBytes returns typed array
This commit is contained in:
Igor Zhukov 2014-10-27 20:18:45 +03:00
parent 44d7747ade
commit ceec0fce2a
6 changed files with 81 additions and 31 deletions

View File

@ -161,7 +161,8 @@ function convertToArrayBuffer(bytes) {
if (bytes instanceof ArrayBuffer) {
return bytes;
}
if (bytes.buffer !== undefined) {
if (bytes.buffer !== undefined &&
bytes.buffer.byteLength == bytes.length * bytes.BYTES_PER_ELEMENT) {
return bytes.buffer;
}
return bytesToArrayBuffer(bytes);

View File

@ -10,17 +10,27 @@ angular.module('izhukov.mtproto', ['izhukov.utils'])
.factory('MtpDcConfigurator', function () {
var dcOptions = Config.Modes.test
? [
{id: 1, host: '173.240.5.253', port: 80},
{id: 2, host: '149.154.167.40', port: 80},
{id: 3, host: '174.140.142.5', port: 80}
{id: 1, url: 'http://173.240.5.253'},
{id: 2, url: 'http://149.154.167.40'},
{id: 3, url: 'http://174.140.142.5'}
]
: (location.protocol == 'https:'
? [
{id: 1, url: 'https://pluto.web.telegram.org'},
{id: 2, url: 'https://venus.web.telegram.org'},
{id: 3, url: 'https://aurora.web.telegram.org'},
{id: 4, url: 'https://vesta.web.telegram.org'},
{id: 5, url: 'https://flora.web.telegram.org'}
]
: [
{id: 1, host: '173.240.5.1', port: 80},
{id: 2, host: '149.154.167.51', port: 80},
{id: 3, host: '174.140.142.6', port: 80},
{id: 4, host: '149.154.167.91', port: 80},
{id: 5, host: '149.154.171.5', port: 80}
];
{id: 1, url: 'http://173.240.5.1'},
{id: 2, url: 'http://149.154.167.51'},
{id: 3, url: 'http://174.140.142.6'},
{id: 4, url: 'http://149.154.167.91'},
{id: 5, url: 'http://149.154.171.5'}
]);
var sslSubdomains = 'pluto,venus,aurora,vesta,flora';
var chosenServers = {};
@ -31,7 +41,7 @@ angular.module('izhukov.mtproto', ['izhukov.utils'])
for (i = 0; i < dcOptions.length; i++) {
dcOption = dcOptions[i];
if (dcOption.id == dcID) {
chosenServer = dcOption.host + ':' + dcOption.port;
chosenServer = dcOption.url;
}
}
chosenServers[dcID] = chosenServer;
@ -197,7 +207,7 @@ angular.module('izhukov.mtproto', ['izhukov.utils'])
var requestData = xhrSendBuffer ? resultBuffer : resultArray,
requestPromise;
try {
requestPromise = $http.post('http://' + MtpDcConfigurator.chooseServer(dcID) + '/apiw1', requestData, {
requestPromise = $http.post(MtpDcConfigurator.chooseServer(dcID) + '/apiw1', requestData, {
responseType: 'arraybuffer',
transformRequest: null
});
@ -791,7 +801,7 @@ angular.module('izhukov.mtproto', ['izhukov.utils'])
longPoll: true
}).then(function () {
delete self.longPollPending;
$timeout(self.checkLongPoll.bind(self), 0);
setZeroTimeout(self.checkLongPoll.bind(self));
}, function () {
console.log('Long-poll failed');
});
@ -948,7 +958,7 @@ angular.module('izhukov.mtproto', ['izhukov.utils'])
MtpNetworker.prototype.performSheduledRequest = function() {
// console.trace('sheduled', this.dcID, this.iii);
// console.log(dT(), 'sheduled', this.dcID, this.iii);
if (this.offline) {
console.log(dT(), 'Cancel sheduled');
return false;
@ -1126,7 +1136,9 @@ angular.module('izhukov.mtproto', ['izhukov.utils'])
};
MtpNetworker.prototype.getDecryptedMessage = function (msgKey, encryptedData) {
// console.log(dT(), 'get decrypted start');
return this.getMsgKeyIv(msgKey, false).then(function (keyIv) {
// console.log(dT(), 'after msg key iv');
return CryptoWorker.aesDecrypt(encryptedData, keyIv[0], keyIv[1]);
});
};
@ -1162,7 +1174,7 @@ angular.module('izhukov.mtproto', ['izhukov.utils'])
responseType: 'arraybuffer',
transformRequest: null
});
requestPromise = $http.post('http://' + MtpDcConfigurator.chooseServer(self.dcID) + '/apiw1', requestData, options);
requestPromise = $http.post(MtpDcConfigurator.chooseServer(self.dcID) + '/apiw1', requestData, options);
} catch (e) {
requestPromise = $q.reject(e);
}
@ -1206,6 +1218,7 @@ angular.module('izhukov.mtproto', ['izhukov.utils'])
encryptedData = deserializer.fetchRawBytes(responseBuffer.byteLength - deserializer.getOffset(), true, 'encrypted_data');
return this.getDecryptedMessage(msgKey, encryptedData).then(function (dataWithPadding) {
// console.log(dT(), 'after decrypt');
var deserializer = new TLDeserialization(dataWithPadding, {mtproto: true});
var salt = deserializer.fetchIntBytes(64, false, 'salt');
@ -1216,6 +1229,7 @@ angular.module('izhukov.mtproto', ['izhukov.utils'])
var messageBody = deserializer.fetchRawBytes(false, true, 'message_data');
// console.log(dT(), 'before hash');
var hashData = convertToUint8Array(dataWithPadding).subarray(0, deserializer.getOffset());
return CryptoWorker.sha1Hash(hashData).then(function (dataHash) {
@ -1223,6 +1237,7 @@ angular.module('izhukov.mtproto', ['izhukov.utils'])
console.warn(msgKey, bytesFromArrayBuffer(dataHash));
throw new Error('server msgKey mismatch');
}
// console.log(dT(), 'after hash check');
var buffer = bytesToArrayBuffer(messageBody);
var deserializerOptions = {
@ -1243,7 +1258,7 @@ angular.module('izhukov.mtproto', ['izhukov.utils'])
}
if (this.offset != offset + result.bytes) {
console.warn(dT(), 'set offset', this.offset, offset, result.bytes);
console.log(dT(), result);
// console.log(dT(), result);
this.offset = offset + result.bytes;
}
// console.log(dT(), 'override message', result);
@ -1262,6 +1277,7 @@ angular.module('izhukov.mtproto', ['izhukov.utils'])
var deserializer = new TLDeserialization(buffer, deserializerOptions);
var response = deserializer.fetchObject('', 'INPUT');
// console.log(dT(), 'after fetch');
return {
response: response,
@ -1294,12 +1310,16 @@ angular.module('izhukov.mtproto', ['izhukov.utils'])
return false;
}
// console.log('shedule req', delay);
// console.log(dT(), 'shedule req', delay);
// console.trace();
$timeout.cancel(this.nextReqPromise);
if (delay > 0) {
this.nextReqPromise = $timeout(this.performSheduledRequest.bind(this), delay || 0);
} else {
setZeroTimeout(this.performSheduledRequest.bind(this))
}
this.nextReqPromise = $timeout(this.performSheduledRequest.bind(this), delay || 0);
this.nextReq = nextReq;
};

View File

@ -464,7 +464,7 @@ angular.module('izhukov.mtproto.wrapper', ['izhukov.utils', 'izhukov.mtproto'])
fileDownload: true,
createNetworker: true
});
}, 6).then(function (result) {
}, 2).then(function (result) {
writeFilePromise.then(function () {
if (canceled) {
return $q.when();

View File

@ -33,7 +33,7 @@ angular.module('izhukov.utils', [])
})
.service('FileManager', function ($window, $timeout, $q) {
.service('FileManager', function ($window, $q) {
$window.URL = $window.URL || $window.webkitURL;
$window.BlobBuilder = $window.BlobBuilder || $window.WebKitBlobBuilder || $window.MozBlobBuilder;
@ -98,10 +98,10 @@ angular.module('izhukov.utils', [])
else {
try {
var blob = blobConstruct([bytesToArrayBuffer(bytes)]);
fileWriter.write(blob);
} catch (e) {
deferred.reject(e);
}
fileWriter.write(blob);
}
return deferred.promise;
@ -150,7 +150,7 @@ angular.module('izhukov.utils', [])
return false;
}
blobParts.push(blob);
$timeout(function () {
setZeroTimeout(function () {
if (fakeFileWriter.onwriteend) {
fakeFileWriter.onwriteend();
}
@ -493,7 +493,7 @@ angular.module('izhukov.utils', [])
finalizeTask = function (taskID, result) {
var deferred = awaiting[taskID];
if (deferred !== undefined) {
console.log(dT(), 'CW done');
// console.log(dT(), 'CW done');
deferred.resolve(result);
delete awaiting[taskID];
}
@ -524,7 +524,7 @@ angular.module('izhukov.utils', [])
}
function performTaskWorker (task, params, embed) {
console.log(dT(), 'CW start', task);
// console.log(dT(), 'CW start', task);
var deferred = $q.defer();
awaiting[taskID] = deferred;
@ -562,7 +562,6 @@ angular.module('izhukov.utils', [])
},
aesEncrypt: function (bytes, keyBytes, ivBytes) {
if (aesNaClEmbed) {
// aesEncryptSync(bytes, keyBytes, ivBytes);
return performTaskWorker('aes-encrypt', {
bytes: addPadding(convertToArrayBuffer(bytes)),
keyBytes: convertToArrayBuffer(keyBytes),
@ -575,7 +574,6 @@ angular.module('izhukov.utils', [])
},
aesDecrypt: function (encryptedBytes, keyBytes, ivBytes) {
if (aesNaClEmbed) {
// aesDecryptSync(encryptedBytes, keyBytes, ivBytes);
return performTaskWorker('aes-decrypt', {
encryptedBytes: addPadding(convertToArrayBuffer(encryptedBytes)),
keyBytes: convertToArrayBuffer(keyBytes),

View File

@ -95,4 +95,37 @@ if (!Function.prototype.bind) {
return fBound;
};
}
}
/* setZeroTimeout polyfill, from http://dbaron.org/log/20100309-faster-timeouts */
(function(global) {
var timeouts = [];
var messageName = 'zero-timeout-message';
function setZeroTimeout(fn) {
timeouts.push(fn);
global.postMessage(messageName, '*');
}
function handleMessage(event) {
if (event.source == global && event.data == messageName) {
event.stopPropagation();
if (timeouts.length > 0) {
var fn = timeouts.shift();
fn();
}
}
}
global.addEventListener('message', handleMessage, true);
var originalSetTimeout = global.setTimeout;
global.setTimeout = function (callback, delay) {
if (!delay || delay <= 5) {
return setZeroTimeout(callback);
}
return originalSetTimeout(callback, delay);
};
global.setZeroTimeout = setZeroTimeout;
})(this);

View File

@ -402,10 +402,8 @@ TLDeserialization.prototype.fetchBytes = function (field) {
(this.byteView[this.offset++] << 16);
}
var bytes = [];
for (var i = 0; i < len; i++) {
bytes.push(this.byteView[this.offset++]);
}
var bytes = this.byteView.subarray(this.offset, this.offset + len);
this.offset += len;
// Padding
while (this.offset % 4) {