imanel/websocket-rack

How to properly cleanup app details inside on_close event?

Closed this issue · 6 comments

Inside the on_open event I am opening an AMQP channel to a RabbitMQ server. I'm saving the channel object in an instance var (@channel) during the on_open event.

Once the web socket is closed, I am attempting to close the AMQP channel (@channel) in the on_close event. However, most of the time, the @channel var doesn't exist when I try to close it.

How can I cleanup app details like this inside on_close?

In looking thru the environment hash (env) that is available to all four events (on_open, on_close, on_error, on_message) in the gem, I see an item named "HTTP_SEC_WEBSOCKET_KEY". This appears to be an unique identifier for the socket connection itself, based on what I am seeing here: http://en.wikipedia.org/wiki/WebSocket.

At the class level, could/should I maintain a list of [web socket key/rabbit channel id] pairs? Is that websocket key dependable? Assuming nothing flaky is happening with that key, in the on_close event I could use the current websocket key to lookup the pair in the class array and then use the found channel id to properly close down the channel.

Is this train of thought inadvisable for some reason?

You should check if @channel variable is set - please remember that on_close event can be called when on_open wasn't ever called(unfortunately this is in specification - I don't like it either) if there is error in handshake - so no valid connection was set(and thus no on_open event called) but client will disconnect then on_close will be triggered. In all other cases there should be match between then as every call since 'on_open' till 'on_close' is handled within single instance(so there is no need to detecting sec_websocket_key)

Regarding idea of 'HTTP_SEC_WEBSOCKET_KEY' - this one is selected randomly by client and is not verified by server so there could be some duplicates. Because of that this is not good idea. If you want to track current connection you can do it in couple ways:

  • set instance variable in 'on_open' and use it to track
  • check self.object_id, as this will be instance of current websocket connection
  • set some class variable and increment it. Because WebSocket-Rack is event based there will be no problem with it

So, I understand that I should not be using the 'HTTP_SEC_WEBSOCKET_KEY' - good to know.

If I were to maintain a hash as a class var/class instance var that looks like this:
[self.object_id => rabbitmq channel id]

... would that be a reasonable approach?

Obviously, if there are 50 connections, my hash would contain 50 pairs ...

Then I could always be able to use the object_id of the current websocket connection to lookup the correct RabbitMQ channel ID to close...

Yes - this will work. Personally I would prefer to make hash { self.object_id => self } and defined instance variable and reader method for this. Thanks to that solution if in future you will need to access other instance methods between different connections you can always iterate over all connections and call it(as opposed to defining different hash with methods or variables each time)

Is there a reliable way to track the number of open web sockets at any given point in time?

As mentioned in last point - this is method I'm using and it's reliable. You can always get number of connections and iterate over them.

class MyApp < Rack::WebSocket::Application

def self.conections
  @connections ||= {}
end

def on_open
  self.class.connections[self.object_id] = self
end

def on_close
  self.class.connections.delete(self.object_id)
end