I started implementing a daemon in Java. Essentially all our devices will connect to it and wait for commands over TCP/IP. Additionally it will offer a web service REST API over HTTP, so that administrators can send/receive data from the devices. This is basically a relaying architecture between devices and administrators. It overcomes any network topology problem (i.e. NAT traversals.) with performance penalty and bandwidth costs of relaying. Web service API is going to be totally self-sufficient, such that totally static HTML pages with Javascript can interact with it.

I’ve implemented the skeleton of the daemon, devices connect to it and it also offers web service API. I tested the web service API by simply requesting the web service URL from the browser as I would do any other URL and confirmed that the correct (JSON) response is given. It was time to see how it is to build a web UI for it. Given its reputation and apparent support, I chose Dojo to implement the web UI with totally static HTML pages. Here’s my experience.

Documentation

From the main Dojo site, it was stated that latest stable release was 1.4.0, and it was the default download link. So I grabbed it. By looking at an example in the demo section, I get an Ajax query working in seconds, only to find out that it is not working for me. Instead of a GET request it was sending OPTIONS request, more on this later. Obviously, I wanted to look at the documentation. Clicking on the Documentation link on the main Dojo site takes you to a place where documentation for 1.4.0 is not offered.

Luckily there were a handful of helpful folks in #dojo @ irc.freenode.net, whom told me that new documentation UI is on its way.

The first one was inaccurate by the time I’m writing this Dojo.xhrGet property list was quite short. I found doc-staging to be more accurate and since it is documentation rather than just reference it also offered much more detail.

Dojo.xhrGet results in OPTIONS request instead of GET

The firs thing I’ve tried with Dojo was obviously the Ajax API.

function getText() {
  dojo.xhrGet({
    url: "http://localhost:8182/hello",
    load: function(responseObject, ioArgs){
      return responseObject;
    },
    error: function(response, ioArgs){
      dojo.byId("toBeReplaced").innerHTML =
        "An error occurred, with response: " + response;
      return response;
    },
    handleAs: "json"
  });
}

This code snippet is taken from Dojo examples which can be found in the official web site. I removed the content of the first function though, it was supposed to do DOM manipulation obviously.

When I run this code, I noticed OPTIONS request in my daemon’s logs. When I was requesting the same URL by writing it to the address bar of the browser all I see was GET requests in my logs, as expected.

Then I’ve tried a pure JS implementation.

var client = new XMLHttpRequest();
client.onreadystatechange = handler;
client.open("GET", "http://192.168.1.94:8182/hello");
client.send();

With this simple implementation I started seeing GET requests in my server as expected. So Dojo should be doing something in a different way. I walked through the Dojo code, thanks to Firebug.  But it turned out that the code is indeed very similar to the regular JS code and there were no obvious bugs, as I expected. Then, I examined the HTTP requests via invaluable Wireshark.

Here’s what I got for Dojo request.

OPTIONS /hello HTTP/1.1
Host: 192.168.1.94:8182
Connection: keep-alive
User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_2; en-US) AppleWebKit/532.5 (KHTML, like Gecko) Chrome/4.0.249.49 Safari/532.5
Cache-Control: max-age=0
Access-Control-Request-Method: POST
Origin: file://
Access-Control-Request-Headers: X-Prototype-Version, X-Requested-With, Content-type, Accept
Accept: */*
Accept-Encoding: gzip,deflate
Accept-Language: tr-TR,tr;q=0.8,en-US;q=0.6,en;q=0.4
Accept-Charset: ISO-8859-9,utf-8;q=0.7,*;q=0.3

And here’s what a regular JS XHR looks like.

GET /hello HTTP/1.1
Host: 192.168.1.94:8182
Connection: keep-alive
User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_2; en-US) AppleWebKit/532.5 (KHTML, like Gecko) Chrome/4.0.249.49 Safari/532.5
Cache-Control: max-age=0
Origin: file://
Accept: */*
Accept-Encoding: gzip,deflate
Accept-Language: tr-TR,tr;q=0.8,en-US;q=0.6,en;q=0.4
Accept-Charset: ISO-8859-9,utf-8;q=0.7,*;q=0.3

Obviously the difference is Access-Control-* properties. I tracked down the source of this was the lines from 10474 to 10477 of dojo.js

 // FIXME: is this appropriate for all content types?
10474 xhr.setRequestHeader("Content-Type", args.contentType || _defaultContentType);
10475 if(!args.headers || !("X-Requested-With" in args.headers)){
10476 xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
10477 }

Funny thing, there’s a FIXME there :) Anyway, when I commented out these lines the request headers were the same for both pure JS Ajax implementation and the Dojo Ajax implementation. And both are now sending GET requests as expected.

I consulted the folks at #dojo about this problem and at first they couldn’t create the problem. Than I’ve stated that the web UI is hosted at X and the web service API of the deamon was at X:P. So it is a cross-domain request. To be precise the script was hosted at http://192.168.1.94/test.html and web service API of the daemon was available at http://192.168.1.94:8182/hello. sfoster from #dojo generously spent some time to test this situation and he also confirmed that OPTIONS requests were being sent. Apparently when Access-Control-* headers are set and it is a cross-domain request browsers decide to send OPTIONS request instead of GET. This is tested with Chrome and Firefox.

Though I believe Access-Control-* properties are there for a good reason. This same problem could also be demonstrated on prototype javascript framework, apparently they are taking the same approach on this.

I’m not sure what is the best practice on this yet, I’ll try to consult some core Dojo developers about this and figure it out.