UNPKG

w-comor-websocket

Version:

A websocket communicator in nodejs and browser. Mapping functions from nodejs to other end points, like a simple RPC.

366 lines (272 loc) 10.3 kB
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>WComorWebsocketServer.mjs - Documentation</title> <script src="scripts/prettify/prettify.js"></script> <script src="scripts/prettify/lang-css.js"></script> <!--[if lt IE 9]> <script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script> <![endif]--> <link type="text/css" rel="stylesheet" href="styles/prettify.css"> <link type="text/css" rel="stylesheet" href="styles/jsdoc.css"> <script src="scripts/nav.js" defer></script> <meta name="viewport" content="width=device-width, initial-scale=1.0"> </head> <body> <input type="checkbox" id="nav-trigger" class="nav-trigger" /> <label for="nav-trigger" class="navicon-button x"> <div class="navicon"></div> </label> <label for="nav-trigger" class="overlay"></label> <nav > <h2><a href="index.html">Home</a></h2><h3>Global</h3><ul><li><a href="global.html#WComorWebsocketClient">WComorWebsocketClient</a></li><li><a href="global.html#WComorWebsocketServer">WComorWebsocketServer</a></li></ul> </nav> <div id="main"> <h1 class="page-title">WComorWebsocketServer.mjs</h1> <section> <article> <pre class="prettyprint source linenums"><code>import WebSocket, { WebSocketServer } from 'ws' import keys from 'lodash-es/keys.js' import get from 'lodash-es/get.js' import genPm from 'wsemi/src/genPm.mjs' import haskey from 'wsemi/src/haskey.mjs' import urlParse from 'wsemi/src/urlParse.mjs' import j2o from 'wsemi/src/j2o.mjs' import isfun from 'wsemi/src/isfun.mjs' import arrHas from 'wsemi/src/arrHas.mjs' /** * 建立WebSocket伺服器 * * @param {Object} opt 輸入設定參數物件 * @param {Integer} [opt.port=8080] 輸入WebSocket伺服器所在port,預設8080 * @param {Function} opt.authenticate 輸入使用者身份認證函數,供伺服器端驗證之用,函數會傳入使用者端連線之token參數,回傳為Promise,resolve(true)為驗證通過,resolve(false)為驗證不通過 * @param {Object} [opt.funcs={}] 輸入伺服器端供使用者端呼叫之函數物件,各key為函數名稱,對應value為函數本體。各函數之輸入需為單一物件,而各函數回傳皆為Promise,可通過resolve與reject回傳結果,預設{} * @example * * import WComorWebsocketServer from 'w-comor-websocket/dist/w-comor-websocket-server.umd.js' * * function random(min, max) { * return Math.floor(Math.random() * max) + min * } * * let opt = { * port: 8080, * authenticate: function(token) { * //使用token驗證使用者身份 * return new Promise(function(resolve, reject) { * setTimeout(function() { * resolve(true) * }, 1000) * }) * }, * filterFuncs: function(token, funcs) { * //使用token驗證使用者身份與過濾可用funcs * return new Promise(function(resolve, reject) { * funcs = funcs.filter(function(v) { * return v.indexOf('Hide') &lt; 0 * }) * resolve(funcs) * }) * }, * onClientChange: function(clients, opt) { * console.log(`Server[port:${opt.port}] now clients: ${clients.length}`) * }, * funcs: { * 'group.plus': function({ p1, p2 }) { * return new Promise(function(resolve, reject) { * setTimeout(function() { * resolve(p1 * p2) * }, random(100, 3000)) * }) * }, * 'group.div': function({ p1, p2 }) { * return new Promise(function(resolve, reject) { * setTimeout(function() { * resolve(p1 / p2) * }, random(100, 3000)) * }) * }, * 'add': function({ p1, p2 }) { * return new Promise(function(resolve, reject) { * setTimeout(function() { * resolve(p1 + p2) * }, random(100, 3000)) * }) * }, * 'addHide': function({ p1, p2 }) { * return new Promise(function(resolve, reject) { * setTimeout(function() { * resolve(p1 + p2) * }, random(100, 3000)) * }) * }, * 'minu': function({ p1, p2 }) { * return new Promise(function(resolve, reject) { * setTimeout(function() { * resolve(p1 - p2) * }, random(100, 3000)) * }) * }, * }, * } * * new WComorWebsocketServer(opt) * */ function WComorWebsocketServer(opt) { //default if (!opt.port) { opt.port = 8080 } //funcs let funcs = [] if (haskey(opt, 'funcs')) { funcs = keys(opt['funcs']) } //authenticate function authenticate(token) { let pm = genPm() if (isfun(opt.authenticate)) { opt.authenticate(token) .then(function(vd) { pm.resolve(vd) }) } else { pm.resolve(true) } return pm } //serverSettings let serverSettings = { port: opt.port, verifyClient: function(info, done) { //data let data = urlParse(info.req.url) //token let token = get(data, 'token', '') //vd authenticate(token) .then(function(vd) { //callback done(vd) }) }, perMessageDeflate: { zlibDeflateOptions: { // See zlib defaults. chunkSize: 1024, memLevel: 7, level: 3 }, zlibInflateOptions: { chunkSize: 10 * 1024 }, // Other options settable: clientNoContextTakeover: true, // Defaults to negotiated value. serverNoContextTakeover: true, // Defaults to negotiated value. serverMaxWindowBits: 10, // Defaults to negotiated value. // Below options specified as default values. concurrencyLimit: 10, // Limits zlib concurrency for perf. threshold: 1024 // Size (in bytes) below which messages // should not be compressed if context takeover is disabled. }, } //wss let wss = new WebSocketServer(serverSettings) //connection let clients = [] wss.on('connection', function(wsc, req) { //console.log('connection', wsc) //console.log('connection', req.connection.remoteAddress) //push clients.push(wsc) if (isfun(opt.onClientChange)) { opt.onClientChange(clients, opt) } //execFunction async function execFunction(data) { //console.log(`Server[port:${opt.port}]: `, data) //token let token = get(data, 'token', '') //vd let vd = await authenticate(token) //check if (vd) { //func let func = get(data, 'func', '') //input let input = get(data, 'input') //getFuncs if (func === 'getFuncs') { if (isfun(opt.filterFuncs)) { funcs = await opt.filterFuncs(token, funcs) } //add output data['output'] = { sys: 'sys', funcs } } //call else if (arrHas(funcs, func)) { //call func in opt.funcs let output = await opt['funcs'][func](input) //add output data['output'] = output } else { //add output data['output'] = { err: `can not find: ${func}` } } } else { //add output data['output'] = { err: `can not authenticate token: ${token}` } } //delete input, 因input可能很大故回傳數據不包含原input delete data['input'] //send if (wsc.readyState === WebSocket.OPEN) { wsc.send(JSON.stringify(data), function(err) { if (err) { console.log(`Server: send output error: ${err}`) } }) } } //message wsc.on('message', function(message, isBinary) { // console.log('received: %s', message, isBinary) //ws伺服器端改收到utf8的buffer, 得轉換成字串才能j2o message = Buffer.from(message).toString('utf8') //data let data = j2o(message) //execFunction execFunction(data) }) //close wsc.on('close', function(message) { //刪除ws clients = clients.filter(function(wst) { return wst !== wsc }) if (isfun(opt.onClientChange)) { opt.onClientChange(clients, opt) } }) }) console.log(`Server running at: ws://localhost:${opt.port}`) } export default WComorWebsocketServer </code></pre> </article> </section> </div> <br class="clear"> <footer> Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.2</a> on Sun Mar 24 2024 22:44:35 GMT+0800 (台北標準時間) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme. </footer> <script>prettyPrint();</script> <script src="scripts/polyfill.js"></script> <script src="scripts/linenumber.js"></script> </body> </html>