How to use http compression with Savon

Doing a lot of SOAP requests using Savon in Rails I wondered, if it is possible to reduce the size of the SOAP responses using http compression (gzip or deflate). Short answer, it is. Long answer, you have to know how to enable it and there a multiple ways to do the trick.

The following is based on Savon 1.0, so if you are using Savon >= 2, things may have changed.

First some background information. In version 0.7.9 Savon added support for gzip compression, so I first tried this.

Savon::Client.new "http://mydomain/myService?wsdl", :gzip => true

Unfortunately, that doesn’t work and Savon complains about wrong argument number. Digging into Savons code it showed, that you can only pass a block as the second parameter. But what do put in there ?

Savon internally uses HTTPI to abstract several Ruby http clients. When you want to mess with http in Savon, you have to mess with HTTPI. Now back to the question what do put in Savons Client block to enable http compression ? The answer is, from inside the block, you can access ‘http’, which is in fact an instance of HTTPI::Request.

HTTPI::Request provides some methods to set and alter the requests http headers. That means, setting http header options for a Savon Client would look like this

Savon::Client.new "http://mydomain/myService?wsdl" do
  http.headers = { 'Accept-Encoding' => 'gzip, deflate' }
end

Of cause, you can set other headers this way, too. It’s just a hash.

HTTPI::Request offers a shortcut method for setting the http header to indicate http compression. It’s called ‘gzip’. So our code from above code also be written like this.

Savon::Client.new "http://mydomain/myService?wsdl" do
  http.gzip
end

Ok, we are done. Quit simple if you know where to put it 🙂

Last but not least you could also enable compression ‘per request’ using Savons soap request hook. Savon offers exactly one hook called :soap_request. To let the documentation speak, it acts like an around filter wrapping the POST request executed to call a SOAP service.

The benefit of interfering the request using this hook is, that you can enable http compression ‘globally’ for all instances of Savon::Client.

Savon.configure do |config|
  config.hooks.define(:enable_compression, :soap_request) do |callback, request|
    # we have to use request.http instead of http
    request.http.gzip
    # and trigger to actual request on our own
    response = callback.call
  end
end

Using http compression the size of my SOAP responses could be noticeably reduced. In fact some compressed responses are only 1/10 of their original size. So this is a very cheap option to save bandwidth and maybe speed up request processing.

How to use http compression with Savon