Twitter not sending 304s

The twitter JSON API that I’ve been using for my status widget has a caching problem, which has caused it to be broken in Opera for a while now. Opera is quite aggressive in re-using its cache (which IMHO is a good thing). However, bad things happen when webservices deviate from the HTTP cache validation model. Twitter is recognising that the browser should be hitting its cache, but its response is broken.

Here’s how it goes:

  1. Load up the JSON document for the first time with an empty cache.
  2. Twitter sends a Last-Modified header and the expected JSON document.
  3. Refresh the document (in Opera, hit enter in the address bar as opposed to clicking the Reload button, since the latter forces a cache refresh). Opera sends an If-Modified-Since header.
  4. Twitter (presumably) recognises that the last status update was not after the browser’s cache timestamp. It sends a degenerate response entity: “[]“; an empty array in javascript, with a 200 OK status.

To test this from a shell:

url='http://twitter.com/statuses/user_timeline/p00ya.json?count=1&callback=f'
lm="$(wget --debug $url 2>&1 \
 |grep '^Last-Modified:' \
 |sed -e 's/Last-Modified/If-Modified-Since/' \
 |tr -d '\n\r')"
[ x != "x$lm" ] && \
  wget -nv --save-headers -O - --header="$lm" $url

The brokeness comes from the half-baked response. A 200 OK status code would be fine if the full JSON object was written out. A 304 status code with any kind of entity would be fine too. The empty array might even prevent breakage in user-agents that don’t handle 304s (but do send If-Modified-Since? wtf?). Sending a 200 response overwrites the correct cached entity, replacing it with the degenerate response.

Firefox seems to be unaffected since it doesn’t cache the document at all and so doesn’t send the If-Modified-Since header.

One workaround is to use something like jQuery’s cache breaking capability (where it adds some random tokens to the URL each time). I refuse. Just remember the widget breakage wasn’t my fault!

A separate issue is that it seemed to break Opera quite badly. Perhaps it was because I’m using jQuery’s ready event, but Opera hangs as if the XHR was synchronous. The document wouldn’t receive any events (no mouse-wheel scrolling). I’ve got no idea why though; my callback functions are robust enough to handle being passed the empty array twitter calls them with, and I wasn’t getting any exceptions.

Browser wars… 2008-style

So, Firefox 3.0 and Opera 9.50 were released within 5 days of each other (June 12 and June 17 respectively). It’s good to see both browsers being progressive and targetting bragging rights such as Acid3, even if they only score 70 and 83 respectively. I notice neither of them pays due attention to deferred scripts, but I guess everyone is using AJAX nowadays instead?

As for the other browsers… I’ve got no problems with Safari. It’s a competent browser, but doesn’t have Opera’s cache/connection leverage or FF’s dev features. I doubt MSIE will even display this post and I don’t care. So, there’s the state of the art.

The firefox upgrade was weird because the RC3 “Check for Updates…” function assured me that I had the latest version. This was further confused by the lack of any RC markings or build numbers in the “About Mozilla Firefox” dialog. It became apparent that I had silent upgrades on, but I don’t recall ever being informed that FF had patched itself.

One thing I’ve noticed is that the DOM Inspector still doesn’t conform to the LnF of the updated Firefox interface. I’d assumed this was a detail that would get polished before the final.

While we’re in a browser-technologies-mood: here’s my rdb opensearch definition for rolldabeats. For all your DnB discography needs.

Opera have munged the keyboard shortcuts. The change seems to be for the better as it’s unified the Windows/UNIX/Mac sets by using guard conditions like Platform Mac. They’ve also deprecated single-key shortcuts, which is fair enough because if you had a textbox focused then they were useless and confusing.

One thing that tripped me up was the Feature ExtendedShortcuts guard, which is activated by the “Enable single-key shortcuts” checkbox in the preferences. The shift+I shortcut for toggling images between cached, all and none was under this guard—but it’s two keys, right?