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) { if (bytes instanceof ArrayBuffer) {
return bytes; return bytes;
} }
if (bytes.buffer !== undefined) { if (bytes.buffer !== undefined &&
bytes.buffer.byteLength == bytes.length * bytes.BYTES_PER_ELEMENT) {
return bytes.buffer; return bytes.buffer;
} }
return bytesToArrayBuffer(bytes); return bytesToArrayBuffer(bytes);

View File

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

View File

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

View File

@ -96,3 +96,36 @@ if (!Function.prototype.bind) {
return fBound; 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); (this.byteView[this.offset++] << 16);
} }
var bytes = []; var bytes = this.byteView.subarray(this.offset, this.offset + len);
for (var i = 0; i < len; i++) { this.offset += len;
bytes.push(this.byteView[this.offset++]);
}
// Padding // Padding
while (this.offset % 4) { while (this.offset % 4) {