Warning: this is an extremely technical post! But since this bug drove me nuts for a little while, I do have to expose it to the WoT community 😉
Finalizing the open-sourcing of our framework for making RFID tags part of the Web (more about that here!), I was testing it the whole day on various browsers.
Firefox, fine! Opera, fine! IE, fine! Chrome, what the heck is that page!?
As you might know, a resource in REST is not bound to a format (or representation as they call it). Rather, a well-done RESTful API should be able to serve several representations depending on the client. A mashup client would for instance be better off with JSON rather than XML or HTML. A browser, would rather render HTML or XHTML. To express this wish the client uses a standard HTTP 1.1 mechanism called “content-negotiation” which, by the way, is a nice, standard and rather lightweight system for things and smart gateways to serve different formats.
Now, to negotiate the format it wants, a browser uses an HTTP header field called “Accept” Header, for instance this is what Firefox 3.6.12 sends:
Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
I.e., “Hello I’m Firefox, a browser, and I mostly speak HTML. I can also understand XML but like it less (0.9).” Nice!
Now, here is what Chrome or any WebKit based-browser (e.g., Safari) will say:
Accept application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
I.e., “Hello I’m Chrome and I love XML (1, default) like less HTML (0.9)”. Duh! That’s a lot less nice since it means that any well-done RESTful API, that uses HTML and not XHTML as a human representation, will return XML to Safari, and XML (except in its XHTML form) is not exactly as human friendly as HTML!
Now, as an API developer this makes you wish the WebKit developers (i.e., builder of one of the biggest client of the HTTP standard) would have read this example of the HTTP 1.1. RFC:
Accept: audio/*; q=0.2, audio/basic SHOULD be interpreted as "I prefer audio/basic, but send me any audio type if it is the best available after an 80% mark-down in quality."
This might not seem like a huge problem but in fact is extremely annoying and I’m not the only one to complain about it. If browsers do not respect the rules of content-negotiation it makes the mechanism pretty useless…
In our case we found a workaround thanks to Jersey (a great framework for RESTful applications in Java) upon which our RFID framework is based. Jersey support a server-side “qs” (Quality of Source) parameter (not part of the HTTP standard but tolerated and understood by many Web-servers such as Apache). Thus, for each resource producing HTML we add the:
@Produces({"text/html;qs=2", ...})
annotation which means that the server will prefer text/html if the client understands it. Nice, except that this overrides the client wishes. If a client sends:
text/html;q=0.2,application/xml;q=1
Then the server will not listen to the client preference (q=1 for application/xml) and will send HTML since the client understands it.
That kind of bug illustrates it, standards are only as good as the people implementing them, and BIG players (for the records Web Kit is the engine of browsers of any Android OR iPhone smart phone!) sometimes just get lost as well (for the records again, even IE does implement it the right way…).
By the way, if any of you already had this issue please share your workaround!