您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

RPCSocketServer.js 4.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. const http = require("http");
  4. const bsock = require("bsock");
  5. const uuid = require("uuid/v4");
  6. /* Responses */
  7. class Response {
  8. constructor(message) {
  9. this.message = message;
  10. }
  11. }
  12. exports.Response = Response;
  13. class SuccessResponse extends Response {
  14. constructor(message) {
  15. super(message);
  16. this.result = "Success";
  17. }
  18. }
  19. exports.SuccessResponse = SuccessResponse;
  20. class ErrorResponse extends Response {
  21. constructor(message = "Unknown error") {
  22. super(message);
  23. this.result = "Error";
  24. }
  25. }
  26. exports.ErrorResponse = ErrorResponse;
  27. class SubscriptionResponse extends SuccessResponse {
  28. constructor(uid, message) {
  29. super(message);
  30. this.uid = uid;
  31. }
  32. }
  33. exports.SubscriptionResponse = SubscriptionResponse;
  34. exports.rpcToRpcinfo = (rpc, owner) => {
  35. switch (rpc.type) {
  36. case "call":
  37. return {
  38. owner: owner,
  39. argNames: extractArgs(rpc.func),
  40. type: rpc.type,
  41. visibility: rpc.visibility,
  42. name: rpc.name,
  43. func: rpc.func,
  44. };
  45. case "unhook":
  46. return {
  47. owner: owner,
  48. argNames: extractArgs(rpc.func),
  49. type: rpc.type,
  50. visibility: rpc.visibility,
  51. name: rpc.name,
  52. func: rpc.func,
  53. };
  54. case "hook":
  55. const generator = hookGenerator(rpc);
  56. return {
  57. owner: owner,
  58. argNames: extractArgs(generator(undefined)),
  59. type: rpc.type,
  60. visibility: rpc.visibility,
  61. name: rpc.name,
  62. unhook: rpc.unhook,
  63. generator: generator,
  64. };
  65. }
  66. };
  67. exports.rpcHooker = (socket, owner, RPCs, makeUnique = true) => {
  68. const suffix = makeUnique ? "-" + uuid().substr(0, 4) : "";
  69. return RPCs.map(rpc => exports.rpcToRpcinfo(rpc, owner))
  70. .map(info => {
  71. const ret = info;
  72. ret.uniqueName = info.name + suffix;
  73. switch (info.type) {
  74. case "hook":
  75. socket.hook(ret.uniqueName, info.generator(socket));
  76. break;
  77. default:
  78. socket.hook(ret.uniqueName, info.func);
  79. }
  80. socket.on('close', () => socket.unhook(info.name));
  81. return ret;
  82. });
  83. };
  84. const hookGenerator = (rpc) => {
  85. const argsArr = extractArgs(rpc.func);
  86. argsArr.pop();
  87. const args = argsArr.join(',');
  88. return eval(`(socket) => async (` + args + `) => {
  89. const res = await rpc.func(` + args + (args.length !== 0 ? ',' : '') + ` (x) => {
  90. socket.call(res.uid, x)
  91. })
  92. if(res.result == 'Success'){
  93. socket.on('close', async () => {
  94. const unhookRes = await rpc.unhook(res.uid)
  95. console.log("Specific close handler for", rpc.name, res.uid, unhookRes)
  96. })
  97. }
  98. return res
  99. }`);
  100. };
  101. const extractArgs = (f) => {
  102. let fn = String(f);
  103. let args = fn.substr(0, fn.indexOf(")"));
  104. args = args.substr(fn.indexOf("(") + 1);
  105. let ret = args.split(",");
  106. return ret;
  107. };
  108. class RPCSocketServer {
  109. constructor(port, rpcExporters = [], conf = {
  110. errorHandler: (socket) => (error) => { socket.destroy(); console.error(error); },
  111. closeHandler: (socket) => () => { console.log("Socket closing"); },
  112. connectionHandler: (socket) => { console.log("New websocket connection in port " + socket.port); }
  113. }) {
  114. this.port = port;
  115. this.rpcExporters = rpcExporters;
  116. this.conf = conf;
  117. this.io = bsock.createServer();
  118. this.wsServer = http.createServer();
  119. this.startWebsocket();
  120. }
  121. startWebsocket() {
  122. try {
  123. this.io.attach(this.wsServer);
  124. this.io.on('socket', (socket) => {
  125. socket.on('error', this.conf.errorHandler(socket));
  126. socket.on('close', this.conf.closeHandler(socket));
  127. this.initApis(socket);
  128. });
  129. this.wsServer.listen(this.port);
  130. }
  131. catch (e) {
  132. //@ts-ignore
  133. this.errorHandler(undefined)("Unable to connect to socket");
  134. }
  135. }
  136. initApis(socket) {
  137. const adminRPCs = [
  138. {
  139. name: 'info',
  140. type: 'call',
  141. visibility: 'private',
  142. func: async () => rpcInfos
  143. }
  144. ];
  145. const rpcInfos = [
  146. ...exports.rpcHooker(socket, "Admin", adminRPCs, false),
  147. ...this.rpcExporters.flatMap(exporter => exports.rpcHooker(socket, exporter.name, exporter.exportRPCs()))
  148. ];
  149. }
  150. }
  151. exports.RPCSocketServer = RPCSocketServer;