Browse Source

use nodejs to connect websocket to replace angularjs websocket library

MaysWind 3 years ago
parent
commit
e4fd45c6eb

+ 2 - 2
app/scripts/services/aria2WebSocketRpcService.js

@@ -1,7 +1,7 @@
 (function () {
     'use strict';
 
-    angular.module('ariaNg').factory('aria2WebSocketRpcService', ['$q', '$websocket', '$timeout', 'ariaNgConstants', 'ariaNgSettingService', 'ariaNgLogService', function ($q, $websocket, $timeout, ariaNgConstants, ariaNgSettingService, ariaNgLogService) {
+    angular.module('ariaNg').factory('aria2WebSocketRpcService', ['$q', 'ariaNgNativeElectronService', '$timeout', 'ariaNgConstants', 'ariaNgSettingService', 'ariaNgLogService', function ($q, ariaNgNativeElectronService, $timeout, ariaNgConstants, ariaNgSettingService, ariaNgLogService) {
         var websocketStatusConnecting = 0;
         var websocketStatusOpen = 1;
 
@@ -76,7 +76,7 @@
         var getSocketClient = function (context) {
             if (socketClient === null) {
                 try {
-                    socketClient = $websocket(rpcUrl, {
+                    socketClient = ariaNgNativeElectronService.createWebSocketClient(rpcUrl, {
                         maxTimeout: 1 // ms
                     });
 

+ 78 - 0
app/scripts/services/ariaNgNativeElectronService.js

@@ -49,6 +49,8 @@
             ariaNgLogService.error(msg, obj);
         });
 
+        invokeMainProcessMethod('on-render-electron-service-inited');
+
         return {
             getRuntimeEnvironment: function () {
                 return invokeMainProcessMethodSync('render-sync-get-runtime-environment');
@@ -156,6 +158,82 @@
 
                 return deferred.promise;
             },
+            createWebSocketClient: function (rpcUrl, options) {
+                var WebSocketClient = function (rpcUrl, options) {
+                    var openCallback = null;
+                    var closeCallback = null;
+                    var messageCallback = null;
+
+                    Object.defineProperty(WebSocketClient.prototype, 'readyState', {
+                        get: function get() {
+                            return invokeMainProcessMethodSync('render-get-websocket-readystate');
+                        },
+                        set: function set() {
+                            throw new Error('The \"readyState\" property is readonly.');
+                        }
+                    });
+
+                    this.send = function (request) {
+                        invokeMainProcessMethod('render-send-websocket-message', {
+                            url: rpcUrl,
+                            data: request
+                        });
+                    };
+
+                    this.reconnect = function () {
+                        invokeMainProcessMethod('render-reconnect-websocket', rpcUrl, options);
+                    };
+
+                    this.onOpen = function (callback) {
+                        openCallback = callback;
+                    };
+
+                    this.onClose = function (callback) {
+                        closeCallback = callback;
+                    };
+
+                    this.onMessage = function (callback) {
+                        messageCallback = callback;
+                    };
+
+                    onMainProcessEvent('on-main-websocket-open', function (event, e) {
+                        if (e.url !== rpcUrl) {
+                            ariaNgLogService.debug('[ariaNgNativeElectronService.websocket.onOpen] event dropped, because rpc url not equals, excepted url: ' + rpcUrl + ", actual url: " + e.url);
+                            return;
+                        }
+
+                        if (angular.isFunction(openCallback)) {
+                            openCallback(e);
+                        }
+                    });
+
+                    onMainProcessEvent('on-main-websocket-close', function (event, e) {
+                        if (e.url !== rpcUrl) {
+                            ariaNgLogService.debug('[ariaNgNativeElectronService.websocket.onClose] event dropped, because rpc url not equals, excepted url: ' + rpcUrl + ", actual url: " + e.url);
+                            return;
+                        }
+
+                        if (angular.isFunction(closeCallback)) {
+                            closeCallback(e);
+                        }
+                    });
+
+                    onMainProcessEvent('on-main-websocket-message', function (event, message) {
+                        if (message.url !== rpcUrl) {
+                            ariaNgLogService.debug('[ariaNgNativeElectronService.websocket.onMessage] event dropped, because rpc url not equals, excepted url: ' + rpcUrl + ", actual url: " + message.url, message.data);
+                            return;
+                        }
+
+                        if (angular.isFunction(messageCallback)) {
+                            messageCallback(message);
+                        }
+                    });
+
+                    invokeMainProcessMethod('render-connect-websocket', rpcUrl, options);
+                };
+
+                return new WebSocketClient(rpcUrl, options);
+            },
             openProjectLink: function () {
                 invokeMainProcessMethod('render-open-external-url', 'https://github.com/mayswind/AriaNg-Native');
             },

+ 5 - 0
main/ipc/events.js

@@ -106,6 +106,10 @@ let notifyRenderProcessNewNewTaskFromTextAfterViewLoaded = function (text) {
     });
 };
 
+let onRenderProcessElectronServiceInited = function (callback) {
+    ipcMain.on('on-render-electron-service-inited', callback);
+};
+
 let onRenderProcessNewDropFile = function (callback) {
     ipcMain.on('on-render-new-drop-file', callback);
 };
@@ -128,6 +132,7 @@ module.exports = {
     notifyRenderProcessNewTaskFromText: notifyRenderProcessNewTaskFromText,
     notifyRenderProcessNewNewTaskFromFileAfterViewLoaded: notifyRenderProcessNewNewTaskFromFileAfterViewLoaded,
     notifyRenderProcessNewNewTaskFromTextAfterViewLoaded: notifyRenderProcessNewNewTaskFromTextAfterViewLoaded,
+    onRenderProcessElectronServiceInited: onRenderProcessElectronServiceInited,
     onRenderProcessNewDropFile: onRenderProcessNewDropFile,
     onRenderProcessNewDropText: onRenderProcessNewDropText
 }

+ 42 - 0
main/ipc/methods.js

@@ -7,6 +7,7 @@ const config = require('../config/config');
 const menu = require('../components/menu');
 const tray = require('../components/tray');
 const http = require('../lib/http');
+const websocket = require('../lib/websocket');
 const localfs = require('../lib/localfs');
 const bittorrent = require('../lib/bittorrent');
 
@@ -105,6 +106,47 @@ ipcMain.handle('render-request-http', (event, requestContext) => {
     return http.request(requestContext);
 });
 
+ipcMain.on('render-connect-websocket', (event, rpcUrl, options) => {
+    websocket.connect(rpcUrl, options,
+        function onOpen(context) {
+            core.mainWindow.webContents.send('on-main-websocket-open', {
+                url: context.url
+            });
+        },
+        function onClose(context) {
+            core.mainWindow.webContents.send('on-main-websocket-close', {
+                url: context.url
+            });
+        },
+        function onMessage(context) {
+            core.mainWindow.webContents.send('on-main-websocket-message', {
+                success: context.success,
+                url: context.url,
+                data: context.message
+            });
+        }
+    );
+});
+
+ipcMain.on('render-reconnect-websocket', (event, rpcUrl, options) => {
+    websocket.reconnect(rpcUrl, options);
+});
+
+ipcMain.on('render-send-websocket-message', (event, requestContext) => {
+    websocket.send(requestContext)
+        .catch(function () {
+            core.mainWindow.webContents.send('on-main-websocket-message', {
+                success: false,
+                url: requestContext.url,
+                data: null
+            });
+        });
+});
+
+ipcMain.on('render-get-websocket-readystate', (event) => {
+    event.returnValue = websocket.getReadyState();
+});
+
 ipcMain.on('render-open-external-url', (event, url) => {
     shell.openExternal(url);
 });

+ 132 - 0
main/lib/websocket.js

@@ -0,0 +1,132 @@
+'use strict';
+
+const WebSocket = require('ws');
+
+let wsClient = null;
+let sendQueue = [];
+
+let fireSendQueue = function () {
+    while (sendQueue.length && wsClient && wsClient.readyState === WebSocket.OPEN) {
+        const request = sendQueue.shift();
+
+        wsClient.send(request.data);
+        request.deferred.resolve();
+    }
+};
+
+let clearSendQueue = function () {
+    for (let i = sendQueue.length - 1; i >= 0; i--) {
+        sendQueue[i].deferred.reject();
+        sendQueue.splice(i, 1);
+    }
+};
+
+let init = function () {
+    if (sendQueue.length) {
+        clearSendQueue();
+    }
+
+    if (wsClient) {
+        wsClient.onopen = null;
+        wsClient.onclose = null;
+        wsClient.onmessage = null;
+        wsClient.onerror = null;
+        wsClient.terminate();
+        wsClient = null;
+    }
+};
+
+let connect = function (rpcUrl, options, onOpenCallback, onCloseCallback, onMessageCallback) {
+    init();
+
+    wsClient = new WebSocket(rpcUrl);
+
+    wsClient.onopen = function () {
+        onOpenCallback({
+            client: wsClient,
+            url: rpcUrl
+        });
+        fireSendQueue();
+    };
+
+    wsClient.onclose = function () {
+        onCloseCallback({
+            client: wsClient,
+            url: rpcUrl
+        });
+    };
+
+    wsClient.onmessage = function (event) {
+        let message = null;
+
+        if (event) {
+            message = event.data;
+        }
+
+        onMessageCallback({
+            client: wsClient,
+            url: rpcUrl,
+            success: true,
+            message: message
+        });
+    };
+
+    wsClient.onerror = function (event) {
+        // Do Nothing
+    };
+};
+
+let reconnect = function (rpcUrl, options) {
+    if (!wsClient) {
+        return;
+    }
+
+    const onOpenFn = wsClient.onopen;
+    const onCloseFn = wsClient.onclose;
+    const onMessageFn = wsClient.onmessage;
+    const onErrorFn = wsClient.onerror;
+
+    init();
+
+    wsClient = new WebSocket(rpcUrl);
+    wsClient.onopen = onOpenFn;
+    wsClient.onclose = onCloseFn;
+    wsClient.onmessage = onMessageFn;
+    wsClient.onerror = onErrorFn;
+};
+
+let send = function (requestContext) {
+    const deferred = {};
+    deferred.promise = new Promise(function (resolve, reject) {
+        deferred.resolve = resolve
+        deferred.reject = reject
+    });
+
+    sendQueue.push({
+        url: requestContext.url,
+        data: requestContext.data,
+        deferred: deferred
+    });
+
+    if (wsClient && wsClient.readyState === WebSocket.OPEN) {
+        fireSendQueue();
+    }
+
+    return deferred.promise;
+};
+
+let getReadyState = function () {
+    if (!wsClient) {
+        return null;
+    }
+
+    return wsClient.readyState;
+};
+
+module.exports = {
+    init: init,
+    connect: connect,
+    reconnect: reconnect,
+    send: send,
+    getReadyState: getReadyState
+};

+ 5 - 0
main/main.js

@@ -14,6 +14,7 @@ const menu = require('./components/menu');
 const tray = require('./components/tray');
 const file = require('./lib/file');
 const page = require('./lib/page');
+const websocket = require('./lib/websocket');
 const ipcEvents = require('./ipc/events');
 require('./ipc/methods');
 
@@ -221,6 +222,10 @@ app.on('ready', () => {
         core.mainWindow = null;
     });
 
+    ipcEvents.onRenderProcessElectronServiceInited((event) => {
+        websocket.init();
+    });
+
     ipcEvents.onRenderProcessNewDropFile((event, arg) => {
         if (!arg) {
             return;

+ 16 - 9
package-lock.json

@@ -508,6 +508,17 @@
       "requires": {
         "angular": "*",
         "ws": "^1.0.0"
+      },
+      "dependencies": {
+        "ws": {
+          "version": "1.1.5",
+          "resolved": "https://registry.npmjs.org/ws/-/ws-1.1.5.tgz",
+          "integrity": "sha512-o3KqipXNUdS7wpQzBHSe180lBGO60SoK0yVo3CYJgb2MkobuWuBX6dhkYP5ORCLd55y+SaflMOV5fqAB53ux4w==",
+          "requires": {
+            "options": ">=0.0.5",
+            "ultron": "1.0.x"
+          }
+        }
       }
     },
     "angularjs-dragula": {
@@ -2826,7 +2837,7 @@
     "options": {
       "version": "0.0.6",
       "resolved": "https://registry.npmjs.org/options/-/options-0.0.6.tgz",
-      "integrity": "sha1-7CLTEoBrtT5zF3Pnza788cZDEo8="
+      "integrity": "sha512-bOj3L1ypm++N+n7CEbbe473A414AB7z+amKYshRb//iuL3MpdDCLhPnw6aVTdKB9g5ZRVHIEp8eUln6L2NUStg=="
     },
     "os-browserify": {
       "version": "0.3.0",
@@ -3662,7 +3673,7 @@
     "ultron": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.0.2.tgz",
-      "integrity": "sha1-rOEWq1V80Zc4ak6I9GhTeMiy5Po="
+      "integrity": "sha512-QMpnpVtYaWEeY+MwKDN/UdKlE/LsFZXM5lO1u7GaZzNgmIbGixHEmVMIKT+vqYOALu3m5GYQy9kz4Xu4IVn7Ow=="
     },
     "umd": {
       "version": "3.0.3",
@@ -3806,13 +3817,9 @@
       "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
     },
     "ws": {
-      "version": "1.1.5",
-      "resolved": "https://registry.npmjs.org/ws/-/ws-1.1.5.tgz",
-      "integrity": "sha512-o3KqipXNUdS7wpQzBHSe180lBGO60SoK0yVo3CYJgb2MkobuWuBX6dhkYP5ORCLd55y+SaflMOV5fqAB53ux4w==",
-      "requires": {
-        "options": ">=0.0.5",
-        "ultron": "1.0.x"
-      }
+      "version": "8.9.0",
+      "resolved": "https://registry.npmjs.org/ws/-/ws-8.9.0.tgz",
+      "integrity": "sha512-Ja7nszREasGaYUYCI2k4lCKIRTt+y7XuqVoHR44YpI49TtryyqbqvDMn5eqfW7e6HzTukDRIsXqzVHScqRcafg=="
     },
     "xmlbuilder": {
       "version": "15.1.1",

+ 4 - 2
package.json

@@ -80,14 +80,16 @@
     "natural-compare": "1.4.0",
     "parse-torrent": "9.1.5",
     "sweetalert": "^1.1.3",
+    "ws": "^8.9.0",
     "yargs": "^15.4.1"
   },
   "mainDependencies": [
     "axios",
     "electron-localshortcut",
     "electron-store",
-    "yargs",
-    "parse-torrent"
+    "parse-torrent",
+    "ws",
+    "yargs"
   ],
   "build": {
     "appId": "net.mayswind.ariang",