Sapan Diwakar

Software developer

Follow me on Twitter Check out my code on GitHub View some of my designs on Dribbble Take a look at my Linked In profile

Compressing WebSocket/ActionCable frames on Rails with permessage-deflate

WebSockets are great for a long-running connection with the client. Can we make them better, faster maybe? The answer is yes and it's very simple. This post describes how to make those WebSocket connections feel even snappier.

HTTP

You have probably already heard about compression and are using it for the HTTP requests. If not, let's get it out of the way first because it's so simple:

config.middleware.insert_after ActionDispatch::Static, Rack::Deflater  

Believe it or not, this line in your config could compress your web responses by as much as 80%. Rack::Deflater middleware compresses the responses using gzip. The middleware automatically detects when compression is supported and allowed. For example, no transformation is made when a cache directive of 'no-transform' is present, or when the response status code is one that doesn't allow an entity body.

WebSockets

Now that we have the compression enabled on HTTP, let's see how to enable this on WebSocket frames. Again, it's not very complex as there is already a spec for permessage-deflate extension to compress WebSocket frames. There is already a good implementation of this extension for ruby and it's very easy to use it in the app.

There's slightly more work to do if you are using ActionCable though since it doesn't have an easy way to add an extension to it's WebSocket driver. Put this somewhere it is picked up during initialization (e.g. config/initializers/action_cable.rb):

module Connection  
  class ClientSocket
    alias_method :old_initialize, :initialize
     def initialize(env, event_target, event_loop, protocols)
      old_initialize(env, event_target, event_loop, protocols)
      @driver.add_extension(PermessageDeflate)
    end
  end
end  

This would reopen the Connection::ClientSocket class and override it's initialize to inject our extension. Now, it wouldn't be right to put it out here without a word of caution, so here goes: As we are Monkey Patching something into core Rails API, it might break things in the future.