webstomp-client icon indicating copy to clipboard operation
webstomp-client copied to clipboard

expose a function to return the readyState of the socket

Open mschipperheyn opened this issue 9 years ago • 12 comments

It's useful to be able to tell whether the socket is active, opening, etc.

WebSocket exposes a readyState variable for this. It would be nice to expose it through the api

mschipperheyn avatar Jun 23 '16 13:06 mschipperheyn

Or maybe just adding a way to get the websocket object underneath webstomp-client. This will avoid to add tons of abstraction just to mimic websocket API.

JSteunou avatar Jun 23 '16 16:06 JSteunou

I'm realizing that I need to create subscribe/unsubscribes outside of the connect callback. So, either I need to tell whether the connection is open or not or, ideally, I could just call the connect method sa often if I wanted, with the socket knowing if it was open or not and immediately returning against the callback with the already open connection or, creating a new connection.

mschipperheyn avatar Jul 04 '16 14:07 mschipperheyn

What I do in my app is maintening a list of subscription. If I'm already connected, I call the client subscribe method, if I'm not, I store the subscription for later.

Maybe that logic can be a part of the webstomp client.

JSteunou avatar Jul 05 '16 08:07 JSteunou

Could you share some of that code as an example. How do you know if you're already connected?

mschipperheyn avatar Jul 05 '16 20:07 mschipperheyn

Sorry I cannot. I just store a simple boolean flag that I switch to true once connected.

JSteunou avatar Jul 05 '16 21:07 JSteunou

I think the websocket.readyState will provider the info

On Tue, Jul 5, 2016 at 6:22 PM Jérôme Steunou [email protected] wrote:

Sorry I cannot. I just store a simple boolean flag that I switch to true once connected.

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/JSteunou/webstomp-client/issues/17#issuecomment-230607223, or mute the thread https://github.com/notifications/unsubscribe/AAXfv0FqCxF1kffR3VGYP1LpQ13erB_Aks5qSsssgaJpZM4I8ylu .

Kind regards,

Marc M.Schipperheyn

mschipperheyn avatar Jul 05 '16 22:07 mschipperheyn

Then why not use stomp.ws.readyState ?

stomp client already exposes the websocket even if it's not so much documented. But using readyState is not comfortable because you have to check its value regularly, where events like onmessage or onerror are triggered as it happens.

JSteunou avatar Jul 06 '16 19:07 JSteunou

yeah, ok. the ideal would be for calling CONNECT to be indemnipotent, so subscribes and sends could always run inside the safe context of the callback.

mschipperheyn avatar Jul 06 '16 22:07 mschipperheyn

Pull request are welcome if you wanna share your idea 😉 Le 7 juil. 2016 00:53, "Marc Schipperheyn" [email protected] a écrit :

yeah, ok. the ideal would be for calling CONNECT to be indemnipotent, so subscribes and sends could always run inside the safe context of the callback.

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/JSteunou/webstomp-client/issues/17#issuecomment-230931813, or mute the thread https://github.com/notifications/unsubscribe/ACNyt3dBNsUj3lsMMjkgVMCSQJg6omp1ks5qTDH4gaJpZM4I8ylu .

JSteunou avatar Jul 07 '16 05:07 JSteunou

Well, what I ended up implementing is a buffering solution, backed by a reconnecting websocket. Not sure if it's a good idea. WDYT?

const socket = class Socket{

  constructor(){}

  createAndConnect(sessionToken, rememberMeToken, onConnectSuccess, onConnectFailure){
    this.createClient(sessionToken, rememberMeToken);
    this.connect(onConnectSuccess, onConnectFailure);
  }

  createClient(sessionToken, authToken){

    console.info(`Connecting to WebSocket: ${config.getWebsocketUrl()}`);

    this.sendBuffer = [];
    this.subscribeBuffer = [];
    this.unsubscribeBuffer = [];
    this.bufferSize = 10;

    let WS = new ReconnectingWebSocket(config.getWebsocketUrl(),{},this.getCookieHeader(sessionToken, authToken), (func) => {
      func();
    });

    WS.debug=config.debugWebsocket;

    this.ws = webstomp.over(
      WS,
      {
        debug:false
      }
    );
  }

  getCookieHeader(sessionToken, authToken){
    if(sessionToken || authToken)
      return {
        'Cookie': ATTR_SEND_SESSIONID + '=' + sessionToken + ';' + ATTR_SEND_REMEMBERME_TOKEN + '=' + authToken
      };

    return {};
  }

  getClient(){
    return this.ws? this.ws : new Error("No websocket present. Call createAndConnect first");
  }

  connect(onConnectSuccess, onConnectFailure){
    var that = this;

    this.headers = {};

    this.ws.connect(this.headers, function(frame){

      onConnectSuccess(frame);

      that.processBuffer.bind(that)();

    }, onConnectFailure);
  }

  disconnect(){
    this.ws.disconnect();
  }

  send(destination, body, bufferable = true){
    if(this.readyState() === WS_STATES.OPEN){
      this.ws.send(destination,body, this.headers);
      return;
    }

    if(bufferable && this.sendBuffer.length <= this.bufferSize){
      this.sendBuffer.push({
        destination,
        body
      });
    }else{
      throw new Error('Trying to send message on closed socket and no buffer available', destination, this.readyState());
    }
  }

  subscribe(destination, callback, bufferable = true){
    if(this.readyState() === WS_STATES.OPEN){
      this.ws.subscribe(destination, (message) => {

        message.ack();

        callback(message);

      }, this.headers);
      return;
    }

    if(bufferable && this.subscribeBuffer.length <= this.bufferSize){

      if(this.subscribeBuffer.filter((el) => el.destination === destination).length === 0)
        this.subscribeBuffer.push({
          destination,
          callback
        });
    }else{
      throw new Error(`Trying to subscribe on closed socket and no buffer available: ${destination}, readyState: ${this.readyState()}, buffer length: ${this.subscribeBuffer.length}`);
    }
  }

  unsubscribe(destination, bufferable = true){
    if(this.readyState() === WS_STATES.OPEN){
      this.ws.unsubscribe(destination);
      return;
    }

    if(bufferable && this.unsubscribeBuffer.length <= this.bufferSize){
      if(this.unsubscribeBuffer.filter((el) => el.destination === destination).length === 0)
        this.unsubscribeBuffer.push({
          destination
        });
    }else{
      throw new Error(`Trying to unsubscribe on closed socket and no buffer available: ${destination}, readyState: ${this.readyState()}, buffer length: ${this.unsubscribeBuffer.length}`);
    }
  }

  begin(transactionId){
    return this.ws.begin(transactionId);
  }

  commit(tx){
    tx.commit();
  }

  abort(tx){
    tx.abort();
  }

  sessionId(){
    return this.ws.sessionId;
  }

  readyState(){
    return this.ws.ws.readyState;
  }

  processBuffer(){
    console.log('Processing websocket buffer', this.sendBuffer.length);

    //Process unsubscribe first, in case a new subscribe came in for the to be unsubscribed endpoint
    for(let action of this.unsubscribeBuffer){
      this.unsubscribe(action.destination);
    }

    for(let action of this.subscribeBuffer){
      this.subscribe(action.destination, action.callback);
    }

    for(let action of this.sendBuffer){
      this.send(action.destination, action.body, false);
    }

    this.sendBuffer = [];
    this.subscribeBuffer = [];
    this.unsubscribeBuffer = [];
  }

}

export {
  socket as default
}

mschipperheyn avatar Jul 07 '16 12:07 mschipperheyn

A PR with a clean diff is more readable

JSteunou avatar Jul 07 '16 12:07 JSteunou

Well, this is just a suggestion. It's not a change to your code

mschipperheyn avatar Jul 07 '16 19:07 mschipperheyn