ClientConnection.js (4783B)
1const Url = require('url'); 2const DeepExtend = require('deep-extend'); 3const Moment = require('moment'); 4 5const GuacdClient = require('./GuacdClient.js'); 6const Crypt = require('./Crypt.js'); 7 8class ClientConnection { 9 10 constructor(server, connectionId, webSocket) { 11 this.STATE_OPEN = 1; 12 this.STATE_CLOSED = 2; 13 14 this.state = this.STATE_OPEN; 15 16 this.server = server; 17 this.connectionId = connectionId; 18 this.webSocket = webSocket; 19 this.query = Url.parse(this.webSocket.upgradeReq.url, true).query; 20 this.lastActivity = Date.now(); 21 this.activityCheckInterval = null; 22 23 this.log(this.server.LOGLEVEL.VERBOSE, 'Client connection open'); 24 25 try { 26 this.connectionSettings = this.decryptToken(); 27 28 this.connectionType = this.connectionSettings.connection.type; 29 30 this.connectionSettings['connection'] = this.mergeConnectionOptions(); 31 32 } catch (error) { 33 this.log(this.server.LOGLEVEL.ERRORS, 'Token validation failed'); 34 this.close(error); 35 return; 36 } 37 38 server.callbacks.processConnectionSettings(this.connectionSettings, (err, settings) => { 39 if (err) { 40 return this.close(err); 41 } 42 43 this.connectionSettings = settings; 44 45 this.log(this.server.LOGLEVEL.VERBOSE, 'Opening guacd connection'); 46 47 this.guacdClient = new GuacdClient(server, this); 48 49 webSocket.on('close', this.close.bind(this)); 50 webSocket.on('message', this.processReceivedMessage.bind(this)); 51 52 if (server.clientOptions.maxInactivityTime > 0) { 53 this.activityCheckInterval = setInterval(this.checkActivity.bind(this), 1000); 54 } 55 }); 56 57 } 58 59 decryptToken() { 60 const crypt = new Crypt(this.server); 61 62 const encrypted = this.query.token; 63 delete this.query.token; 64 65 return crypt.decrypt(encrypted); 66 } 67 68 log(level, ...args) { 69 if (level > this.server.clientOptions.log.level) { 70 return; 71 } 72 73 const stdLogFunc = this.server.clientOptions.log.stdLog; 74 const errorLogFunc = this.server.clientOptions.log.errorLog; 75 76 let logFunc = stdLogFunc; 77 if (level === this.server.LOGLEVEL.ERRORS) { 78 logFunc = errorLogFunc; 79 } 80 81 logFunc(this.getLogPrefix(), ...args); 82 } 83 84 getLogPrefix() { 85 return '[' + Moment().format('YYYY-MM-DD HH:mm:ss') + '] [Connection ' + this.connectionId + '] '; 86 } 87 88 close(error) { 89 if (this.state == this.STATE_CLOSED) { 90 return; 91 } 92 93 if (this.activityCheckInterval !== undefined && this.activityCheckInterval !== null) { 94 clearInterval(this.activityCheckInterval); 95 } 96 97 if (error) { 98 this.log(this.server.LOGLEVEL.ERRORS, 'Closing connection with error: ', error); 99 } 100 101 if (this.guacdClient) { 102 this.guacdClient.close(); 103 } 104 105 this.webSocket.removeAllListeners('close'); 106 this.webSocket.close(); 107 this.server.activeConnections.delete(this.connectionId); 108 109 this.state = this.STATE_CLOSED; 110 111 this.log(this.server.LOGLEVEL.VERBOSE, 'Client connection closed'); 112 } 113 114 error(error) { 115 this.server.emit('error', this, error); 116 this.close(error); 117 } 118 119 processReceivedMessage(message) { 120 this.lastActivity = Date.now(); 121 this.guacdClient.send(message); 122 } 123 124 send(message) { 125 if (this.state == this.STATE_CLOSED) { 126 return; 127 } 128 129 this.log(this.server.LOGLEVEL.DEBUG, '>>>G2W> ' + message + '###'); 130 this.webSocket.send(message, {binary: false, mask: false}, (error) => { 131 if (error) { 132 this.close(error); 133 } 134 }); 135 } 136 137 mergeConnectionOptions() { 138 let unencryptedConnectionSettings = {}; 139 140 Object 141 .keys(this.query) 142 .filter(key => this.server.clientOptions.allowedUnencryptedConnectionSettings[this.connectionType].includes(key)) 143 .forEach(key => unencryptedConnectionSettings[key] = this.query[key]); 144 145 let compiledSettings = {}; 146 147 DeepExtend( 148 compiledSettings, 149 this.server.clientOptions.connectionDefaultSettings[this.connectionType], 150 this.connectionSettings.connection.settings, 151 unencryptedConnectionSettings 152 ); 153 154 return compiledSettings; 155 } 156 157 checkActivity() { 158 if (Date.now() > (this.lastActivity + this.server.clientOptions.maxInactivityTime)) { 159 this.close(new Error('WS was inactive for too long')); 160 } 161 } 162 163 164} 165 166module.exports = ClientConnection;