WebSocketController.js 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  1. const WebSocket = require ('ws');
  2. const _ = require('lodash');
  3. const ReaderWorker = require('../core/Reader/ReaderWorker');//singleton
  4. const ReaderStorage = require('../core/Reader/ReaderStorage');//singleton
  5. const WorkerState = require('../core/WorkerState');//singleton
  6. const utils = require('../core/utils');
  7. const cleanPeriod = 1*60*1000;//1 минута
  8. const closeSocketOnIdle = 5*60*1000;//5 минут
  9. class WebSocketController {
  10. constructor(wss, config) {
  11. this.config = config;
  12. this.readerStorage = new ReaderStorage();
  13. this.readerWorker = new ReaderWorker(config);
  14. this.workerState = new WorkerState();
  15. this.wss = wss;
  16. wss.on('connection', (ws) => {
  17. ws.on('message', (message) => {
  18. this.onMessage(ws, message);
  19. });
  20. });
  21. setTimeout(() => { this.periodicClean(); }, cleanPeriod);
  22. }
  23. periodicClean() {
  24. try {
  25. const now = Date.now();
  26. this.wss.clients.forEach((ws) => {
  27. if (!ws.lastActivity || now - ws.lastActivity > closeSocketOnIdle - 50) {
  28. ws.terminate();
  29. }
  30. });
  31. } finally {
  32. setTimeout(() => { this.periodicClean(); }, cleanPeriod);
  33. }
  34. }
  35. async onMessage(ws, message) {
  36. let req = {};
  37. try {
  38. ws.lastActivity = Date.now();
  39. req = JSON.parse(message);
  40. switch (req.action) {
  41. case 'test':
  42. this.test(req, ws); break;
  43. case 'get-config':
  44. this.getConfig(req, ws); break;
  45. case 'worker-get-state':
  46. this.workerGetState(req, ws); break;
  47. case 'worker-get-state-finish':
  48. this.workerGetStateFinish(req, ws); break;
  49. case 'reader-restore-cached-file':
  50. this.readerRestoreCachedFile(req, ws); break;
  51. default:
  52. throw new Error(`Action not found: ${req.action}`);
  53. }
  54. } catch (e) {
  55. this.send({error: e.message}, req, ws);
  56. }
  57. }
  58. send(res, req, ws) {
  59. if (ws.readyState == WebSocket.OPEN) {
  60. ws.lastActivity = Date.now();
  61. let r = Object.assign({}, res);
  62. if (req.requestId)
  63. r.requestId = req.requestId;
  64. ws.send(JSON.stringify(r));
  65. }
  66. }
  67. //Actions ------------------------------------------------------------------
  68. async test(req, ws) {
  69. this.send({message: 'Liberama project is awesome'}, req, ws);
  70. }
  71. async getConfig(req, ws) {
  72. if (Array.isArray(req.params)) {
  73. this.send(_.pick(this.config, req.params), req, ws);
  74. } else {
  75. throw new Error('params is not an array');
  76. }
  77. }
  78. async workerGetState(req, ws) {
  79. if (!req.workerId)
  80. throw new Error(`key 'workerId' is wrong`);
  81. const state = this.workerState.getState(req.workerId);
  82. this.send((state ? state : {}), req, ws);
  83. }
  84. async workerGetStateFinish(req, ws) {
  85. if (!req.workerId)
  86. throw new Error(`key 'workerId' is wrong`);
  87. const refreshPause = 200;
  88. let i = 0;
  89. let state = {};
  90. while (1) {// eslint-disable-line no-constant-condition
  91. const prevProgress = state.progress || -1;
  92. const prevState = state.state || '';
  93. state = this.workerState.getState(req.workerId);
  94. this.send((state ? state : {}), req, ws);
  95. if (!state) break;
  96. if (state.state != 'finish' && state.state != 'error')
  97. await utils.sleep(refreshPause);
  98. else
  99. break;
  100. i++;
  101. if (i > 2*60*1000/refreshPause) {//2 мин ждем телодвижений воркера
  102. this.send({state: 'error', error: 'Время ожидания процесса истекло'}, req, ws);
  103. }
  104. i = (prevProgress != state.progress || prevState != state.state ? 1 : i);
  105. }
  106. }
  107. async readerRestoreCachedFile(req, ws) {
  108. if (!req.path)
  109. throw new Error(`key 'path' is empty`);
  110. const workerId = this.readerWorker.restoreCachedFile(req.path);
  111. const state = this.workerState.getState(workerId);
  112. this.send((state ? state : {}), req, ws);
  113. }
  114. }
  115. module.exports = WebSocketController;