Cross-Domain Data with Rack and Rails

An undeniable theme from RailsConf this year was the arrival of Rack in Rails as of version 2.3. The first topic that seemed to pique developer interest was Rails Metal. Most enthusiasm centered around the speed bump of using a lighter stack for high volume URIs. While this is a useful upgrade to Rails, the architectural shift provided by Rack brings new flexibility. In this post, I will demonstrate the unique value of mounting Rack middleware in your Rails stack.

Cross-domain Data

Web applications using AJAX to obtain third party data must deal with the browser's same-origin policy. Typical workarounds include either JSONP or the use of a service proxy on the home domain. Both techniques bring trade-offs. JSONP removes the same-origin barrier but leaves the cross-domain security hole in place; a third party service can execute arbitrary JavaScript on the client site. Service proxies offer more safety at the cost of new infrastructure and code that must be maintained.

A Rails app serving API data can now provide a third option without opening security holes or requiring new infrastructure for client sites.

AJACSS Sounds Like AJAX

Asynchronous JavaScript and CSS, also known as CSSHttpRequest, is a method of URI-encoding data in 2KB chunks split over several CSS rules with a modified data URI scheme. Because CSS is not subject to the same-origin policy, no service proxy is required. Moreover, because this is CSS, the security concerns of JSONP no longer apply. Here's an example CSS snippet with an encoded "Hello Rails" message:

#c0 { background: url(data:,Hello%20Rails); }

Shopping for Rack Middleware

If you're in need of a piece of Rack middleware, there are two places to check before rolling your own -- the Rack wiki and rack-contrib on GitHub. In the case of CSSHttpRequest, we can just grab a pre-made piece of middleware and drop it into our Rails app thanks to Cameron Walters from nb.io.

In config/environment.rb, add the following code inside the Rails::Initializer block:

# First specify the required gems so that rake gems:install will install them
config.gem "rack-contrib", :lib => 'rack/contrib/csshttprequest'
config.gem "nbio-csshttprequest", :lib => "csshttprequest", :source => "http://gems.github.com/"

# Next, load Rack::CSSHTTPRequest in your middleware stack
config.middleware.use 'Rack::CSSHTTPRequest'

That's it! Rack::CSSHTTPRequest will automatically encode the output for any request using the "_format=chr" convention used by Rails. Other web apps and services can use the data without exposing security holes or requiring new infrastructure.