Browse Source

Moved MTProto from web worker to service worker

master
morethanwords 4 years ago
parent
commit
dad42da803
  1. 94
      package-lock.json
  2. 2
      package.json
  3. 89
      src/lib/mtproto/mtproto.service.ts
  4. 61
      src/lib/mtproto/mtprotoworker.ts
  5. 33
      src/lib/storage.ts
  6. 2
      src/pages/pageIm.ts
  7. 4
      tsconfig.json
  8. 6
      webpack.common.js

94
package-lock.json generated

@ -3026,6 +3026,12 @@ @@ -3026,6 +3026,12 @@
"integrity": "sha512-SDSGgXT3LRCH6qMWk8OHT1vLSVNuHNvCpKCx2/TYtQMbMGGgxJC9fspwSkQjqzRagrWnCrxuLL3jMNXLXHHvSw==",
"dev": true
},
"@types/anymatch": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/@types/anymatch/-/anymatch-1.3.1.tgz",
"integrity": "sha512-/+CRPXpBDpo2RK9C68N3b2cOvO0Cf5B9aPijHsoDQTHivnGSObdOF2BRQOYjojWTDy6nQvMjmqRXIxH55VjxxA==",
"dev": true
},
"@types/asn1js": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/@types/asn1js/-/asn1js-0.0.1.tgz",
@ -3186,12 +3192,91 @@ @@ -3186,12 +3192,91 @@
"integrity": "sha512-1HcDas8SEj4z1Wc696tH56G8OlRaH/sqZOynNNB+HF0WOeXPaxTtbYzJY2oEfiUxjSKjhCKr+MvR7dCHcEelug==",
"dev": true
},
"@types/serviceworker-webpack-plugin": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@types/serviceworker-webpack-plugin/-/serviceworker-webpack-plugin-1.0.1.tgz",
"integrity": "sha512-NBLpxauF9s0dG+g3KFRo7iQwEuSa9E9Sn+SG/4SxECVOIG/0JBIK3Qc4b+81vH3/1dXJqMfm1JFh8vVsgp5/Dw==",
"dev": true,
"requires": {
"@types/webpack": "*"
}
},
"@types/source-list-map": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/@types/source-list-map/-/source-list-map-0.1.2.tgz",
"integrity": "sha512-K5K+yml8LTo9bWJI/rECfIPrGgxdpeNbj+d53lwN4QjW1MCwlkhUms+gtdzigTeUyBr09+u8BwOIY3MXvHdcsA==",
"dev": true
},
"@types/stack-utils": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-1.0.1.tgz",
"integrity": "sha512-l42BggppR6zLmpfU6fq9HEa2oGPEI8yrSPL3GITjfRInppYFahObbIQOQK3UGxEnyQpltZLaPe75046NOZQikw==",
"dev": true
},
"@types/tapable": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/@types/tapable/-/tapable-1.0.6.tgz",
"integrity": "sha512-W+bw9ds02rAQaMvaLYxAbJ6cvguW/iJXNT6lTssS1ps6QdrMKttqEAMEG/b5CR8TZl3/L7/lH0ZV5nNR1LXikA==",
"dev": true
},
"@types/uglify-js": {
"version": "3.9.3",
"resolved": "https://registry.npmjs.org/@types/uglify-js/-/uglify-js-3.9.3.tgz",
"integrity": "sha512-KswB5C7Kwduwjj04Ykz+AjvPcfgv/37Za24O2EDzYNbwyzOo8+ydtvzUfZ5UMguiVu29Gx44l1A6VsPPcmYu9w==",
"dev": true,
"requires": {
"source-map": "^0.6.1"
},
"dependencies": {
"source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"dev": true
}
}
},
"@types/webpack": {
"version": "4.41.21",
"resolved": "https://registry.npmjs.org/@types/webpack/-/webpack-4.41.21.tgz",
"integrity": "sha512-2j9WVnNrr/8PLAB5csW44xzQSJwS26aOnICsP3pSGCEdsu6KYtfQ6QJsVUKHWRnm1bL7HziJsfh5fHqth87yKA==",
"dev": true,
"requires": {
"@types/anymatch": "*",
"@types/node": "*",
"@types/tapable": "*",
"@types/uglify-js": "*",
"@types/webpack-sources": "*",
"source-map": "^0.6.0"
},
"dependencies": {
"source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"dev": true
}
}
},
"@types/webpack-sources": {
"version": "1.4.2",
"resolved": "https://registry.npmjs.org/@types/webpack-sources/-/webpack-sources-1.4.2.tgz",
"integrity": "sha512-77T++JyKow4BQB/m9O96n9d/UUHWLQHlcqXb9Vsf4F1+wKNrrlWNFPDLKNT92RJnCSL6CieTc+NDXtCVZswdTw==",
"dev": true,
"requires": {
"@types/node": "*",
"@types/source-list-map": "*",
"source-map": "^0.7.3"
},
"dependencies": {
"source-map": {
"version": "0.7.3",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz",
"integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==",
"dev": true
}
}
},
"@types/yargs": {
"version": "13.0.5",
"resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.5.tgz",
@ -16064,6 +16149,15 @@ @@ -16064,6 +16149,15 @@
"send": "0.17.1"
}
},
"serviceworker-webpack-plugin": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/serviceworker-webpack-plugin/-/serviceworker-webpack-plugin-1.0.1.tgz",
"integrity": "sha512-VgDEkZ3pA0HajsRaWtl5w6bLxAXx0Y+4dm7YeTcIxVmvC9YXvstex38HOBDuYETeDS5fUlBy/47gC0QYBrG0nw==",
"dev": true,
"requires": {
"minimatch": "^3.0.4"
}
},
"set-blocking": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",

2
package.json

@ -30,6 +30,7 @@ @@ -30,6 +30,7 @@
"@types/chrome": "0.0.91",
"@types/jest": "^24.9.1",
"@types/puppeteer": "^3.0.1",
"@types/serviceworker-webpack-plugin": "^1.0.1",
"aes-js": "^3.1.2",
"autoprefixer": "^9.8.0",
"babel-jest": "^24.9.0",
@ -61,6 +62,7 @@ @@ -61,6 +62,7 @@
"qr-code-styling": "^1.0.1",
"resolve-url-loader": "^3.1.1",
"sass-loader": "^8.0.2",
"serviceworker-webpack-plugin": "^1.0.1",
"streamsaver": "^2.0.4",
"style-loader": "^1.2.1",
"terser-webpack-plugin": "^3.0.2",

89
src/lib/mtproto/mtproto.worker.js → src/lib/mtproto/mtproto.service.ts

@ -7,8 +7,7 @@ import AppStorage from '../storage'; @@ -7,8 +7,7 @@ import AppStorage from '../storage';
import cryptoWorker from "../crypto/cryptoworker";
import networkerFactory from "./networkerFactory";
//const ctx: Worker = self as any;
const ctx = self;
const ctx = self as any as ServiceWorkerGlobalScope;
//console.error('INCLUDE !!!', new Error().stack);
@ -25,8 +24,8 @@ const ctx = self; @@ -25,8 +24,8 @@ const ctx = self;
* let the calling scope pass in the global scope object.
* @returns {boolean}
*/
var _isSafari = null;
function isSafari(scope) {
var _isSafari: boolean = null;
function isSafari(scope: any) {
if(_isSafari == null) {
var userAgent = scope.navigator ? scope.navigator.userAgent : null;
_isSafari = !!scope.safari ||
@ -35,11 +34,11 @@ function isSafari(scope) { @@ -35,11 +34,11 @@ function isSafari(scope) {
return _isSafari;
}
function isObject(object) {
function isObject(object: any) {
return typeof(object) === 'object' && object !== null;
}
function fillTransfer(transfer, obj) {
function fillTransfer(transfer: any, obj: any) {
if(!obj) return;
if(obj instanceof ArrayBuffer) {
@ -57,10 +56,14 @@ function fillTransfer(transfer, obj) { @@ -57,10 +56,14 @@ function fillTransfer(transfer, obj) {
}
}
function reply() {
/**
* Respond to request
*/
function respond(client: Client | ServiceWorker | MessagePort, ...args: any[]) {
// отключил для всего потому что не успел пофиксить transfer detached
//if(isSafari(self)/* || true */) {
ctx.postMessage(...arguments);
// @ts-ignore
client.postMessage(...args);
/* } else {
var transfer = new Set();
fillTransfer(transfer, arguments);
@ -72,12 +75,24 @@ function reply() { @@ -72,12 +75,24 @@ function reply() {
}
networkerFactory.setUpdatesProcessor((obj, bool) => {
//console.log('updatesss');
//ctx.postMessage({update: {obj, bool}});
reply({update: {obj, bool}});
//respond({update: {obj, bool}});
ctx.clients.matchAll({ includeUncontrolled: false, type: 'window' }).then((listeners) => {
if(!listeners.length) {
//console.trace('no listeners?', self, listeners);
return;
}
listeners[0].postMessage({update: {obj, bool}});
});
});
ctx.onmessage = function(e) {
var taskID = e.data.taskID;
ctx.addEventListener('message', async(e) => {
const taskID = e.data.taskID;
console.log('[SW] Got message:', taskID, e, e.data);
if(e.data.useLs) {
AppStorage.finishTask(e.data.taskID, e.data.args);
@ -87,36 +102,52 @@ ctx.onmessage = function(e) { @@ -87,36 +102,52 @@ ctx.onmessage = function(e) {
switch(e.data.task) {
case 'computeSRP':
case 'gzipUncompress':
// @ts-ignore
return cryptoWorker[e.data.task].apply(cryptoWorker, e.data.args).then(result => {
//ctx.postMessage({taskID: taskID, result: result});
reply({taskID: taskID, result: result});
respond(e.source, {taskID: taskID, result: result});
});
default: {
try {
// @ts-ignore
let result = apiManager[e.data.task].apply(apiManager, e.data.args);
if(result instanceof Promise) {
result.then(result => {
//console.log(e.data.task + ' result:', result, taskID);
reply({taskID: taskID, result: result});
//ctx.postMessage({taskID: taskID, result: result});
}).catch(err => {
//console.error(e.data.task + ' err:', err, taskID);
//ctx.postMessage({taskID: taskID, error: err});
reply({taskID: taskID, error: err});
});
} else {
//ctx.postMessage({taskID: taskID, result: result});
reply({taskID: taskID, result: result});
result = await result;
}
respond(e.source, {taskID: taskID, result: result});
} catch(err) {
reply({taskID: taskID, error: err});
//ctx.postMessage({taskID: taskID, error: err});
respond(e.source, {taskID: taskID, error: err});
}
//throw new Error('Unknown task: ' + e.data.task);
}
}
}
});
ctx.postMessage('ready');
/**
* Service Worker Installation
*/
ctx.addEventListener('install', (event: ExtendableEvent) => {
//console.log('service worker is installing');
/* initCache();
event.waitUntil(
initNetwork(),
); */
event.waitUntil(ctx.skipWaiting()); // Activate worker immediately
});
/**
* Service Worker Activation
*/
ctx.addEventListener('activate', (event) => {
//console.log('service worker activating', ctx);
/* if (!ctx.cache) initCache();
if (!ctx.network) initNetwork(); */
event.waitUntil(ctx.clients.claim());
});

61
src/lib/mtproto/mtprotoworker.ts

@ -1,6 +1,7 @@ @@ -1,6 +1,7 @@
import {dT, isObject, $rootScope} from '../utils';
import AppStorage from '../storage';
import CryptoWorkerMethods from '../crypto/crypto_methods';
import runtime from 'serviceworker-webpack-plugin/lib/runtime';
type Task = {
taskID: number,
@ -8,8 +9,15 @@ type Task = { @@ -8,8 +9,15 @@ type Task = {
args: any[]
};
/* let pending: any[] = [];
function resendPending() {
if(navigator.serviceWorker.controller) {
for(let i = 0; i < pending.length; i++) navigator.serviceWorker.controller.postMessage(pending[i]);
pending = [];
}
} */
class ApiManagerProxy extends CryptoWorkerMethods {
private webWorker: Worker | boolean = false;
private taskID = 0;
private awaiting: {
[id: number]: {
@ -27,8 +35,49 @@ class ApiManagerProxy extends CryptoWorkerMethods { @@ -27,8 +35,49 @@ class ApiManagerProxy extends CryptoWorkerMethods {
super();
console.log(dT(), 'ApiManagerProxy constructor');
if(window.Worker) {
import('./mtproto.worker.js').then((worker: any) => {
/**
* Service worker
*/
runtime.register({ scope: '/' });
navigator.serviceWorker.ready.then((registration) => {
console.info(dT(), 'ApiManagerProxy set SW');
this.releasePending();
});
navigator.serviceWorker.oncontrollerchange = () => {
console.error('oncontrollerchange');
this.releasePending();
navigator.serviceWorker.controller.addEventListener('error', (e) => {
console.error('controller error:', e);
});
};
/**
* Message resolver
*/
navigator.serviceWorker.addEventListener('message', (e) => {
if(!isObject(e.data)) {
return;
}
if(e.data.useLs) {
// @ts-ignore
AppStorage[e.data.task](...e.data.args).then(res => {
navigator.serviceWorker.controller.postMessage({useLs: true, taskID: e.data.taskID, args: res});
});
} else if(e.data.update) {
if(this.updatesProcessor) {
this.updatesProcessor(e.data.update.obj, e.data.update.bool);
}
} else {
this.finalizeTask(e.data.taskID, e.data.result, e.data.error);
}
});
/* if(window.Worker) {
import('./mtproto_service.js').then((worker: any) => {
var tmpWorker = new worker.default();
tmpWorker.onmessage = (e: any) => {
if(!this.webWorker) {
@ -60,7 +109,7 @@ class ApiManagerProxy extends CryptoWorkerMethods { @@ -60,7 +109,7 @@ class ApiManagerProxy extends CryptoWorkerMethods {
this.webWorker = false;
};
});
}
} */
}
private finalizeTask(taskID: number, result: any, error: any) {
@ -93,9 +142,9 @@ class ApiManagerProxy extends CryptoWorkerMethods { @@ -93,9 +142,9 @@ class ApiManagerProxy extends CryptoWorkerMethods {
}
private releasePending() {
if(this.webWorker) {
if(navigator.serviceWorker.controller) {
this.pending.forEach(pending => {
(this.webWorker as Worker).postMessage(pending);
navigator.serviceWorker.controller.postMessage(pending);
});
this.pending.length = 0;

33
src/lib/storage.ts

@ -142,6 +142,8 @@ class AppStorage { @@ -142,6 +142,8 @@ class AppStorage {
private isWebWorker: boolean;
private taskID = 0;
private tasks: {[taskID: number]: (result: any) => void} = {};
//private log = (...args: any[]) => console.log('[SW LS]', ...args);
private log = (...args: any[]) => {};
constructor() {
if(Modes.test) {
@ -149,7 +151,8 @@ class AppStorage { @@ -149,7 +151,8 @@ class AppStorage {
}
// @ts-ignore
this.isWebWorker = typeof WorkerGlobalScope !== 'undefined' && self instanceof WorkerGlobalScope;
//this.isWebWorker = typeof WorkerGlobalScope !== 'undefined' && self instanceof WorkerGlobalScope;
this.isWebWorker = typeof ServiceWorkerGlobalScope !== 'undefined' && self instanceof ServiceWorkerGlobalScope;
}
public setPrefix(newPrefix: string) {
@ -161,6 +164,13 @@ class AppStorage { @@ -161,6 +164,13 @@ class AppStorage {
}
public finishTask(taskID: number, result: any) {
this.log('finishTask:', taskID, result, Object.keys(this.tasks));
if(!this.tasks.hasOwnProperty(taskID)) {
this.log('no such task:', taskID, result);
return;
}
this.tasks[taskID](result);
delete this.tasks[taskID];
}
@ -168,10 +178,25 @@ class AppStorage { @@ -168,10 +178,25 @@ class AppStorage {
private proxy<T>(methodName: string, ..._args: any[]) {
return new Promise<T>((resolve, reject) => {
if(this.isWebWorker) {
this.tasks[this.taskID] = resolve;
const taskID = this.taskID++;
this.tasks[taskID] = resolve;
(self as any as ServiceWorkerGlobalScope)
.clients
.matchAll({ includeUncontrolled: false, type: 'window' })
.then((listeners) => {
if(!listeners.length) {
//console.trace('no listeners?', self, listeners);
return;
}
this.log('will proxy', {useLs: true, task: methodName, taskID, args: _args});
listeners[0].postMessage({useLs: true, task: methodName, taskID, args: _args});
});
// @ts-ignore
self.postMessage({useLs: true, task: methodName, taskID: this.taskID, args: _args});
this.taskID++;
//self.postMessage({useLs: true, task: methodName, taskID: this.taskID, args: _args});
} else {
let args = Array.prototype.slice.call(_args);
args.push((result: T) => {

2
src/pages/pageIm.ts

@ -29,7 +29,7 @@ let onFirstMount = () => { @@ -29,7 +29,7 @@ let onFirstMount = () => {
//(Array.from(document.getElementsByClassName('rp')) as HTMLElement[]).forEach(el => ripple(el));
Array.from(document.getElementsByClassName('btn-menu-toggle')).forEach((el) => {
el.addEventListener('click', (e) => {
(el as HTMLElement).addEventListener('click', (e) => {
//console.log('click pageIm');
if(!el.classList.contains('btn-menu-toggle')) return false;

4
tsconfig.json

@ -5,7 +5,7 @@ @@ -5,7 +5,7 @@
//"target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */
"target": "es2016", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */
"module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
"lib": ["es2016", "dom", "ES2018.Promise"], /* Specify library files to be included in the compilation. */
"lib": ["es2016", "dom", "ES2018.Promise", "webworker"], /* Specify library files to be included in the compilation. */
"allowJs": true, /* Allow javascript files to be compiled. */
// "checkJs": true, /* Report errors in .js files. */
// "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
@ -40,7 +40,7 @@ @@ -40,7 +40,7 @@
// "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
/* Module Resolution Options */
// "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
"moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
// "baseUrl": "./", /* Base directory to resolve non-absolute module names. */
// "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */

6
webpack.common.js

@ -3,6 +3,7 @@ const HtmlWebpackPlugin = require('html-webpack-plugin'); @@ -3,6 +3,7 @@ const HtmlWebpackPlugin = require('html-webpack-plugin');
const MediaQueryPlugin = require('media-query-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const postcssPresetEnv = require('postcss-preset-env');
const ServiceWorkerWebpackPlugin = require('serviceworker-webpack-plugin');
const fs = require('fs');
const allowedIPs = ['195.66.140.39', '192.168.31.144', '127.0.0.1', '192.168.31.1', '192.168.31.192', '176.100.18.181'];
@ -165,6 +166,11 @@ module.exports = { @@ -165,6 +166,11 @@ module.exports = {
sockHost: useLocal ? undefined : 'tweb.enko.club',
},
plugins: [
new ServiceWorkerWebpackPlugin({
entry: path.join(__dirname, 'src/lib/mtproto/mtproto.service.ts'),
filename: 'sw.js',
excludes: ['**/*'],
}),
new HtmlWebpackPlugin({
filename: `index.html`,
//template: 'public/index_template.html',

Loading…
Cancel
Save