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.