Wednesday, May 11, 2011

Cross-origin resource sharing

Today I've stumbled upon quite an interesting topic while watching this video from MIX11. Around 40th minute Giorgio Sardo brought up the topic of cross-site Ajax calls. At first I thought "OK, yet another proprietary extension in IE9" but then I started googling and what I found (and subsequently here) has exceeded my wildest expectations. As it turns out it is implemented in all modern browsers. There's a catch to it: the server needs to be aware that such a request will come and attach a specific HTTP header in response.

Since I don't do well with plain text explanations I've put together a Sinatra client and a Rack server to demonstrate this feature.

Client


This is the client code using jQuery's load method to do the Ajax heavy lifting:
$("body").load("http://localhost:9292/"); 

No surprise there, right? It looks like a regular Ajax request. The difference here is that it queries localhost on a different port (9292 - default Rack port) than the one that the request originated from (4567 - default Sinatra port).

Server


Let's see how we can respond to such a request using a pure Rack application.
run lambda { |env| [ 
200, 
{ 
  'Content-Type'=>'text/html', 
  'Access-Control-Allow-Origin' => env['HTTP_ORIGIN'] || '*',
  'Access-Control-Allow-Headers' => 'X-Requested-With'
}, 
env.inspect 
] }

The key here is the line that defines the Access-Control-Allow-Origin response header. When it's set to * it means that everyone can access this resource from everywhere. It's a sort of catch-all if you will. The value of env['HTTP_ORIGIN'] is the value sent by the browser to the server saying "Hey, I'm calling from here. Can I access your resource please?" And if the server agrees (which it does from everywhere at this point) the browser will honor the response and return the data back to the script. The Access-Control-Allow-Headers header is sometimes required for example by ExtJS' Ajax calls.

Here's the example for your convenience. You can start it by issuing the following commands:
cd server
start ruby -S rackup
cd ..\client
start ruby client.rb

After that open your favorite browser (I sure hope it's not IE < 8), navigate to http://localhost:4567 and observe the environment string from the server dumped onto your screen.
The client is obviously written in Sinatra, the best micro web framework ever and the server is a pure Rack application.

No comments: