216 lines
4.7 KiB
JavaScript
216 lines
4.7 KiB
JavaScript
/* classe client WebSocket */
|
|
class WSClient{
|
|
|
|
/* (1) Constructor
|
|
*
|
|
* @server_url<String> The url of the desired server
|
|
* @channel_uri<String> The url of the desired channel
|
|
*
|
|
---------------------------------------------------------*/
|
|
constructor(server_url, channel_uri){
|
|
|
|
/* (1) Store & create data
|
|
---------------------------------------------------------*/
|
|
/* (1) Store the arguments */
|
|
this.server_url = server_url;
|
|
this.channel_uri = channel_uri;
|
|
|
|
/* (2) Initialise event data */
|
|
this.is_opened = false; // Set to TRUE when connection opened
|
|
this.pending = null; // Next message to send through connection
|
|
this.handler = []; // On reveice handlers
|
|
|
|
/* (3) Create the websocket connection */
|
|
this.ws = new WebSocket(this.server_url+'/'+channel_uri);
|
|
|
|
|
|
/* (2) Bind events
|
|
---------------------------------------------------------*/
|
|
/* (1) OPEN: when connection is established */
|
|
this.ws.onopen = this._onopen.bind(this);
|
|
|
|
/* (2) MESSAGE: when message received */
|
|
this.ws.onmessage = this._onmessage.bind(this);
|
|
|
|
/* (3) ERROR: when websocket error */
|
|
this.ws.onerror = this._onerror.bind(this);
|
|
|
|
/* (4) CLOSE: when websocket closes */
|
|
this.ws.onclose = this._onclose.bind(this);
|
|
|
|
}
|
|
|
|
|
|
/* (2) Binds a handle to the onReceive event (global only)
|
|
*
|
|
* @callback<Function> Callback function(message, error)
|
|
*
|
|
---------------------------------------------------------*/
|
|
listen(callback){
|
|
|
|
/* (1) Check @callback type (default value) */
|
|
if( !(callback instanceof Function) )
|
|
throw new Error('WSClient.bind(callback) expected argument *callback* to be a function');
|
|
|
|
/* (2) Store callback */
|
|
this.on_receive = callback;
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
/* (3) Sends a message through the socket
|
|
*
|
|
* @message<String> The data to send
|
|
* <Object> The data to send (will be serialized)
|
|
*
|
|
---------------------------------------------------------*/
|
|
send(message){
|
|
|
|
/* (1) Manage arguments
|
|
---------------------------------------------------------*/
|
|
/* (2) Check @message type */
|
|
if( typeof message != 'string' && typeof message != 'object' ){
|
|
this.on_receive(null, 'wrong message type');
|
|
return this;
|
|
}
|
|
|
|
/* (2) Stringify JSON if object */
|
|
if( typeof message == 'object' )
|
|
message = JSON.stringify(message);
|
|
|
|
|
|
/* (2) Send process
|
|
---------------------------------------------------------*/
|
|
/* (1) If not opened -> store message (will send when onopen()) */
|
|
if( !this.is_opened )
|
|
this.pending = message;
|
|
|
|
/* (2) If already opened -> send now */
|
|
else
|
|
this.ws.send(message);
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
/* (4) PRIVATE: Open handler
|
|
*
|
|
---------------------------------------------------------*/
|
|
_onopen(){
|
|
|
|
/* (1) Set @is_opened status variable */
|
|
this.is_opened = true;
|
|
|
|
/* (2) If message pending */
|
|
if( this.pending != null ){
|
|
|
|
this.ws.send(this.pending); // send message
|
|
this.pending = null; // reset message (optional)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
/* (5) PRIVATE: Receive handler
|
|
*
|
|
---------------------------------------------------------*/
|
|
_onmessage(msg_event){
|
|
|
|
/* (1) If not trusted -> error */
|
|
if( !msg_event.isTrusted ){
|
|
this.on_receive(null, 'not trusted');
|
|
return;
|
|
}
|
|
|
|
/* (2) If wrong origin -> error */
|
|
if( msg_event.origin != this.server_url ){
|
|
this.on_receive(null, 'unknown origin');
|
|
return;
|
|
}
|
|
|
|
/* (3) If not for this channel -> exit */
|
|
if( msg_event.target.url != this.server_url+'/'+this.channel_uri )
|
|
return;
|
|
|
|
/* (3) Try to JSON parse */
|
|
var parsedMsg = null;
|
|
try{
|
|
|
|
parsedMsg = JSON.parse(msg_event.data);
|
|
|
|
}catch(e){
|
|
this.on_receive(null, 'JSON error');
|
|
return;
|
|
}
|
|
|
|
/* (4) If all right -> success */
|
|
this.on_receive(parsedMsg, null);
|
|
|
|
}
|
|
|
|
|
|
/* (6) PRIVATE: Error handler
|
|
*
|
|
---------------------------------------------------------*/
|
|
_onerror(){
|
|
|
|
/* (1) Remove other handlers (only 1 error) */
|
|
this.ws.onmessage = null;
|
|
this.ws.onclose = null;
|
|
|
|
/* (2) Send error */
|
|
this.on_receive(null, 'websocket error');
|
|
|
|
}
|
|
|
|
|
|
/* (7) PRIVATE: Close handler
|
|
*
|
|
---------------------------------------------------------*/
|
|
_onclose(){
|
|
|
|
/* (1) Remove onmessage (not to catch messages) */
|
|
this.ws.onmessage = null;
|
|
|
|
/* (2) Send end */
|
|
this.on_receive(null, 'websocket closed');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class WSClientBuilder{
|
|
|
|
/* (1) Constructor
|
|
*
|
|
* @server_url<String> The url of the desired server
|
|
*
|
|
---------------------------------------------------------*/
|
|
constructor(server_url){
|
|
/* (1) Store the server url */
|
|
this.server_url = server_url;
|
|
}
|
|
|
|
|
|
/* (2) Builds a WSClient with desired channel
|
|
*
|
|
* @channel_uri<String> The uri of the desired channel
|
|
*
|
|
---------------------------------------------------------*/
|
|
channel(channel_uri=''){
|
|
return new WSClient(this.server_url, channel_uri);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export{ WSClient, WSClientBuilder } |