33const path = require ( 'path' ) ;
44const fs = require ( 'fs' ) ;
55const fastify = require ( 'fastify' ) ;
6- const fastify_static = require ( 'fastify- static' ) ;
7- const fastify_ws = require ( 'fastify-ws ' ) ;
6+ const fastify_static = require ( '@ fastify/ static' ) ;
7+ const fastify_ws = require ( '@ fastify/websocket ' ) ;
88
99//Server
1010const version = "0.10.0" ;
@@ -21,7 +21,7 @@ var Clexi = function(customSettings){
2121 var idIsPassword = customSettings . idIsPassword || settings . idIsPassword || false ;
2222
2323 var sslCertPath = customSettings . sslCertPath || path . join ( __dirname , "ssl" ) ;
24- var wwwPath = customSettings . wwwPath || path . join ( __dirname , "www" ) ;
24+ var wwwPath = path . join ( __dirname , customSettings . wwwPath || "www" ) ;
2525 var defaultXtensionsPath = path . join ( __dirname , 'xtensions' ) ;
2626 var customXtensionsPath = customSettings . customXtensionsPath || defaultXtensionsPath ;
2727 var customXtensions = customSettings . customXtensions || [ ] ;
@@ -49,11 +49,13 @@ var Clexi = function(customSettings){
4949 const server = fastify ( server_options ) ;
5050 ClexiServer . fastify = server ;
5151
52- //Plugins
52+ //Register static files plugin and path
5353 server . register ( fastify_static , {
5454 root : wwwPath ,
55- redirect : false //for fastify 2 security - see: https://github.com/advisories/GHSA-p6vg-p826-qp3v
55+ redirect : false
5656 } ) ;
57+
58+ //Register Websocket plugin
5759 server . register ( fastify_ws ) ;
5860
5961 //Xtensions
@@ -118,32 +120,29 @@ var Clexi = function(customSettings){
118120 //single receiver?
119121 var client = data . receiver ;
120122 delete data . receiver ; //remove object before sending
121- if ( ! idIsPassword || ( idIsPassword && client . authState ) ) {
122- if ( typeof data === "object" ) {
123- client . send ( JSON . stringify ( data ) ) ;
124- } else {
125- client . send ( data ) ;
126- }
127- } else {
128- server . log . error ( "Tried to broadcast to unauthorized client. Client should be disconnected already!" ) ;
129- client . terminate ( ) ;
130- }
123+ sendToClient ( client , data ) ;
131124 } else {
132125 //broadcast to all
133- server . ws . clients . forEach ( function each ( client ) {
134- if ( client . readyState === 1 ) { //WebSocket.OPEN should be 1
135- if ( ! idIsPassword || ( idIsPassword && client . authState ) ) {
136- if ( typeof data === "object" ) {
137- client . send ( JSON . stringify ( data ) ) ;
138- } else {
139- client . send ( data ) ;
140- }
141- } else {
142- server . log . error ( "Tried to broadcast to unauthorized client. Client should be disconnected already!" ) ;
143- client . terminate ( ) ;
126+ if ( server . websocketServer ) {
127+ server . websocketServer . clients . forEach ( function each ( client ) {
128+ if ( client . readyState === 1 ) {
129+ sendToClient ( client , data ) ;
144130 }
145- }
146- } ) ;
131+ } ) ;
132+ }
133+ }
134+ }
135+ function sendToClient ( client , data ) {
136+ if ( ! idIsPassword || ( idIsPassword && client . authState ) ) {
137+ if ( typeof data === "object" ) {
138+ client . send ( JSON . stringify ( data ) ) ;
139+ } else {
140+ client . send ( data ) ;
141+ }
142+ } else {
143+ //make sure all unauthorized clients are removed
144+ server . log . error ( "Tried to broadcast to unauthorized client. Client should be disconnected already!" ) ;
145+ client . terminate ( ) ;
147146 }
148147 }
149148
@@ -198,10 +197,103 @@ var Clexi = function(customSettings){
198197 reply . send ( ) ;
199198 }
200199 }
200+
201+ //Websocket interface
202+ server . register ( async function ( fastify ) {
203+ //default
204+ fastify . get ( '/' , { websocket : true } , ( socket , request ) => {
205+ handleWebsocketReq ( socket , request , '/' ) ;
206+ } ) ;
207+ //popular client path
208+ fastify . get ( '/clexi' , { websocket : true } , ( socket , request ) => {
209+ handleWebsocketReq ( socket , request , '/clexi' ) ;
210+ } ) ;
211+ //fallback for common proxy paths
212+ fastify . get ( '/ws' , { websocket : true } , ( socket , request ) => {
213+ handleWebsocketReq ( socket , request , '/ws' ) ;
214+ } ) ;
215+ } ) ;
216+ function handleWebsocketReq ( socket , request , path ) {
217+ server . log . info ( "Client connected via WebSocket path '" + path + "'." ) ;
218+
219+ socket . on ( 'message' , function ( msg ) {
220+ try {
221+ //msg is a buffer
222+ let msgObj = JSON . parse ( msg . toString ( ) ) ;
223+
224+ //Handle extensions input
225+ if ( msgObj . type && xtensions [ msgObj . type ] ) {
226+ server . log . info ( 'Calling xtensions: ' + msgObj . type ) ;
227+ let response = xtensions [ msgObj . type ] . input ( msgObj , socket ) ;
228+ if ( response ) {
229+ socket . send ( JSON . stringify ( {
230+ response : response ,
231+ type : msgObj . type ,
232+ id : msgObj . id
233+ } ) ) ;
234+ }
235+
236+ //Welcome
237+ } else if ( msgObj . type == "welcome" ) {
238+ //check server ID
239+ if ( msgObj . data && msgObj . data . server_id == serverId ) {
240+ socket . authState = true ;
241+ }
242+ if ( ! idIsPassword ) {
243+ socket . send ( JSON . stringify ( {
244+ type : "welcome" ,
245+ code : 200 ,
246+ info : {
247+ id : serverId ,
248+ version : ( "CLEXI Node.js server v" + version ) ,
249+ xtensions : getXtensionsInfo ( )
250+ }
251+ } ) ) ;
252+ } else if ( idIsPassword && socket . authState ) {
253+ socket . send ( JSON . stringify ( {
254+ type : "welcome" ,
255+ code : 200 ,
256+ info : {
257+ version : ( "CLEXI Node.js server v" + version ) ,
258+ xtensions : getXtensionsInfo ( )
259+ }
260+ } ) ) ;
261+ } else {
262+ socket . send ( JSON . stringify ( {
263+ type : "welcome" ,
264+ code : 401 ,
265+ info : {
266+ msg : "not authorized" ,
267+ version : ( "CLEXI Node.js server v" + version )
268+ }
269+ } ) ) ;
270+ //socket.terminate(); //the client should gracefully disconnect O_O
271+ }
272+
273+ //undefined
274+ } else {
275+ socket . send ( JSON . stringify ( {
276+ response : ( "Unknown message type: " + msgObj . type ) ,
277+ type : "undefined"
278+ } ) ) ;
279+ }
280+ } catch ( e ) {
281+ server . log . error ( "Socket Message Error: " + e . message ) ;
282+ }
283+ } ) ;
284+
285+ socket . on ( 'close' , function ( ) {
286+ server . log . info ( 'Client disconnected.' ) ;
287+ } ) ;
288+
289+ socket . on ( 'error' , function ( e ) {
290+ server . log . error ( `Client error: ${ e . message } ` ) ;
291+ } ) ;
292+ }
201293
202294 //Run the server
203295 ClexiServer . start = function ( ) {
204- server . listen ( port , hostname , function ( err , address ) {
296+ server . listen ( { port : port , host : hostname } , function ( err , address ) {
205297 if ( err ) {
206298 server . log . error ( err ) ;
207299 process . exit ( 1 ) ;
@@ -211,87 +303,12 @@ var Clexi = function(customSettings){
211303 console . log ( `Hostname: ${ hostname } - SSL: ${ useSsl } ` ) ;
212304 server . log . info ( `Server running at ${ address } ` ) ;
213305
214- //Websocket interface
215- server . ws . on ( 'connection' , function ( socket , request ) {
216- server . log . info ( 'Client connected.' ) ;
217-
218- //CLIENT INPUT
219- socket . on ( 'message' , function ( msg ) {
220- let msgObj = JSON . parse ( msg ) ;
221-
222- //Handle extensions input
223- if ( msgObj . type && xtensions [ msgObj . type ] ) {
224- server . log . info ( 'Calling xtensions: ' + msgObj . type ) ;
225- let response = xtensions [ msgObj . type ] . input ( msgObj , socket ) ;
226- if ( response ) {
227- socket . send ( JSON . stringify ( {
228- response : response ,
229- type : msgObj . type ,
230- id : msgObj . id
231- } ) ) ;
232- }
233-
234- //Welcome
235- } else if ( msgObj . type && msgObj . type == "welcome" ) {
236- //check server ID
237- if ( msgObj . data && msgObj . data . server_id && msgObj . data . server_id == serverId ) {
238- socket . authState = true ;
239- }
240- if ( ! idIsPassword ) {
241- socket . send ( JSON . stringify ( {
242- type : "welcome" ,
243- code : 200 ,
244- info : {
245- id : serverId ,
246- version : ( "CLEXI Node.js server v" + version ) ,
247- xtensions : getXtensionsInfo ( )
248- }
249- } ) ) ;
250- } else if ( idIsPassword && socket . authState ) {
251- socket . send ( JSON . stringify ( {
252- type : "welcome" ,
253- code : 200 ,
254- info : {
255- version : ( "CLEXI Node.js server v" + version ) ,
256- xtensions : getXtensionsInfo ( )
257- }
258- } ) ) ;
259- } else {
260- socket . send ( JSON . stringify ( {
261- type : "welcome" ,
262- code : 401 ,
263- info : {
264- msg : "not authorized" ,
265- version : ( "CLEXI Node.js server v" + version )
266- }
267- } ) ) ;
268- //socket.terminate(); //the client should gracefully disconnect O_O
269- }
270-
271- //undefined
272- } else {
273- socket . send ( JSON . stringify ( {
274- response : ( "Unknown message type: " + msgObj . type ) ,
275- type : "undefined"
276- } ) ) ;
277- }
278- } ) ;
279-
280- socket . on ( 'close' , function ( ) {
281- server . log . info ( 'Client disconnected.' ) ;
282- } ) ;
283-
284- socket . on ( 'error' , function ( e ) {
285- server . log . error ( `Client error: ${ e . message } ` ) ;
286- } ) ;
287- } ) ;
288-
289306 //Load extensions
290307 loadXtensions ( ) ;
291308 } ) ;
292309 }
293310
294- ClexiServer . stop = server . close ;
311+ ClexiServer . stop = ( ) => server . close ( ) ;
295312
296313 return ClexiServer ;
297314}
0 commit comments