Savon vs. Handsoap: Errors

This documentation is deprecated, please have a look at savonrb.com!

Both client libraries provide the same default behavior for error handling; they raise distinct exceptions for SOAP and HTTP errors:

  • Handsoap::HttpError / Handsoap::Fault
  • Savon::HTTPError / Savon::SOAPFault

And both clients offer a way to override that behavior. Savon lets you surpress errors globally and provides a simple error handling interface on the response object:

Savon::Response.raise_errors = false
[...]
p response.soap_fault
p response.http_error

The same can be achieved in Handsoap by overwriting error hooks in the service implementation:

def on_fault(fault)
  p fault
end

def on_http_error(response)
  p response
end

Savon vs. Handsoap: Benchmark

This documentation is deprecated, please have a look at savonrb.com!

As every benchmark is only as good as the platform it is performed on, you should execute them on your own machine. The example code provides a rake task for that. Running the benchmarks on a local development machine (MacBook 2.4GH Intel Core 2 Duo) produces the following results:

$ rake benchmark
                                    user     system      total        real
savon dynamic                   0.390000   0.070000   0.460000 ( 17.860459)
savon static                    0.120000   0.040000   0.160000 ( 11.929432)
handsoap                        0.100000   0.020000   0.120000 (  3.962274)
[...]

Since Handsoap uses native C libraries like curb and nokogiri the produced results are not that surprising. Savon on the other hand is based on pure Ruby with only a small set of dependencies.

Handsoap offers a way to change the underlying gems for HTTP connectivity and XML parsing. There are some more benchmarks for these configurations:

[...]
                                    user     system      total        real
handsoap curb nokogiri          0.100000   0.020000   0.120000 (  3.962274)
handsoap curb rexml             0.080000   0.040000   0.120000 (  3.905696)
handsoap curb libxml            0.060000   0.040000   0.100000 (  8.961743)
handsoap net_http nokogiri      0.130000   0.050000   0.180000 (  9.184230)
handsoap net_http rexml         0.100000   0.050000   0.150000 ( 11.429074)
handsoap net_http libxml        0.070000   0.050000   0.120000 (  9.220251)
handsoap httpclient nokogiri    0.310000   0.040000   0.350000 (  4.381711)
handsoap httpclient rexml       0.430000   0.070000   0.500000 (  4.782451)
handsoap httpclient libxml      0.350000   0.040000   0.390000 (  4.209751)

Savon vs. Handsoap: JRuby

This documentation is deprecated, please have a look at savonrb.com!

First of all, both client implementations can be run using JRuby. Since Savon is a pure Ruby implementation you get JRuby support for free. The drawback is, that it’s slow on MRI.
Using native C Libraries by default, Handsoap is bleezing fast. But when it comes to terms of compatibility with JRuby you’ve got to fall back to slow core Ruby classes:

Handsoap.http_driver = :net_http
Handsoap.xml_query_driver = :rexml

Using this configuration nearly elimininates the differences in performance:

$ rake benchmark_jruby
                                    user     system      total        real
savon jruby dynamic            15.060000   0.000000  15.060000 ( 15.060000)
savon jruby static             10.984000   0.000000  10.984000 ( 10.984000)
handsoap jruby                  8.717000   0.000000   8.717000 (  8.717000)

Savon vs. Handsoap: Conclusion

This documentation is deprecated, please have a look at savonrb.com!

The dynamic coding style of Savon is more elegant and the Ruby magic makes it easy to use. Integrating SOAP services is now a fun task!

Handsoap makes a good job on encapsulating SOAP calls and provides a wide spectrum of configuration points. It’s very fast on MRI and you have complete control over the XML and transport layer.

Even though I am a Java guy I love the Savon approach to SOAP, especially because it completely eliminates the XML part!

Many thanx to rubiii for helping with the examples.

Heroku with ruby-aaws

The first step of migrating my Rails app to Heroku included only a reduced feature set.

I completely removed all parts dealing with the Amazon-API, because ruby-aaws could not be installed as a gem on Heroku. The ruby-aaws gem is build on Ruby >= 1.8.7 while Heroku is running 1.8.6 #fail.

I asked Ian Macdonald, the creator of ruby-aaws, if there might be a workaround for this problem. Ian told me that this should not be a blocker and pointed me to some things I should take care of.

How to make ruby-aaws run with 1.8.6

Here are the steps that I performed to get things running:

# install gemsonrails to freeze the gem into the app
sudo gem install gemsonrails

# go to your rails folder
cd rails/yourapp

# install gemsonrails for your app
gemsonrails

# freeze the app
rake gems:freeze GEM=ruby-aaws

This will freeze the gem into vendor/gems/. Then I removed the tests and the examples, because they are not necessary and contain code that won’t work.
The app won’t start up until I fixed some little load path error:

# vendor/gems/ruby-aaws-0.7.0/init.rb
# add amazon to the path
require_options = ["ruby-aaws", "ruby/aaws", "amazon"]

The ruby-aaws library expects some login-credentials for the Amazon-API which are usually stored in a ~/.amazonrc file. Since Heroku gives no access to a user home (at least I did not manage to get access to it), I worked around it by putting the file in the RAILS_ROOT and adding some glue code in Amazon::Config:

# vendor/gems/ruby-aaws-0.7.0/lib/amazon.rb
# add rails-file
config_files << File.join(RAILS_ROOT, '.amazonrc') if defined?(RAILS_ROOT)

The next thing to fix was the usage of String.bytesize, which is not available in 1.8.6:

string.gsub( /([^a-zA-Z0-9_.~-]+)/ ) do
  # replace .bytesize with .size
  '%' + $1.unpack( 'H2' * $1.size ).join( '%' ).upcase
end

Voilà!