Friday, February 8, 2008

Cross-domain XMLHttpRequest

Source: WikipediaDuring my presentation @ Barcamp I was talking about Cross-site XMLHttpRequests and promised I'll write a follow-up on my presentation as well. As any web developer that didn't go to hidding before GMail came to life knows, XMLHttpRequests can be a very helpful tool for your users - sure, there are some accessibility and security issues but, providing that you use it correctly, it can be an enormous benefit to the user experiance.

One thing that plagued XMLHttpRequests (at least if you were a legit user) was same-origin policy that you had to follow. At some point, probably due to a bunch of mails, even Yahoo posted a tutorial on how to resolve this issue with a proxy. But fear no more! Firefox 3 bring us support for Cross-site XMLHttpRequests which provides us with options to control who can access our content with remote Javascript calls. W3C Access Control draft goes into very specific details on how exactly should browsers implament this feature, but I won't cover this in that much of a detail because Firefox 3 still exhibit some problems with it. However, it does bring us a nice new feature that we can play with and give it a try.

My lightning talk was just a quick introduction to this subject - due to time constrains I was also unable to show a practical example (hey, that's the whole idea of lightning talks! :) - and so will be my example which was built with Django. Basically just the code you get when you start a new project and nothing else.

On the client side (i.e. in browser) you code remains pretty much the same as it would be if you would be making same-domain request:

window.onload = function(){
var xhr = new XMLHttpRequest();"POST", "http://localhost:8001/");
xhr.onreadystatechange = function(){
if ( xhr.readyState == 4 ) {
if ( xhr.status == 200 ) {
document.body.innerHTML = "I received: " + xhr.responseText;
} else {
document.body.innerHTML = "Some error occured.";

(Basically I just took John Resig's code for the client side, since he first publish this in the start of January.)

(Also note that
I am making calls from django instance running at port 8000 to the instance running at port 8001 - if you check Mozilla's same-origin policy you will see that different ports are also forbidden.)

Then of course comes the interesting part, the server side code - John published his blog post with PHP code, I will publish it for Django, but since it's so easy, you have to make it working in every language or framework you code/work with. :) Interesting part of course is the control of who can access your code and in what kind of way.

def foo(request):
r = HttpResponse("some content")
r["Access-Control"] = "allow <localhost:8000>"
And that's it! Yes, access control allows whole bunch of interesting features - for example, you can allow all sites [1] to access your resources (probably useful for mashups), limit your access only to specific request types [2], etc.

"allow <*>" [1]
"allow <> method POST" [2]

But during my experimants I discovered that, at least for POST requests, Firefox still returned uncaught exceptions. If anyone has any ideas what went wrong, please don't hesitate to tell me in the comments or mail me so that I can publish it.

Now, if only IE would bring something equally useful...