The Cross-Origin Request Sharing (CORS) knot

As a developer for a company that does a lot of cross-domain posting I was happy to see CORS finally happening. I’ve authored jQuery.windowName plug-in to add support for cross-domain posting in the past and this was my way out – no more hacks as browser support for CORS grows.

Yeah right.

CORS is supported on XMLHttpRequest object in Gecko and Webkit. You just request something on a different domain, and if the response has the right header with the right value you get the data. In IE you have to use a new object called XDomainRequest which is somewhat similar to XMLHttpRequest, but without readystate, onreadystate event handler and basically any header getters/setters.

Sounds ok and when I first tried it I was quite happy with how it worked. But unfortunately nothing is that simple in the land of browsers. When it fails, it’s a whole new game…

Gecko (Firefox)

Same domain request:
  • 200 response: xhr = {status: 200, statusText: ‘OK’}
  • 304 response: xhr = {status: 304, statusText: ‘Not Modified’}
Cross domain request with proper headers:
  • 200 response: xhr = {status: 200, statusText: ‘OK’}
  • 304 response: xhr = {status: 0, statusText: ‘Not Modified’}
Cross domain request without proper headers:
  • 200 response: xhr = {status: 0, statusText: ‘OK’}
  • 304 response: xhr = {status: 0, statusText: ‘Not Modified’}

Webkit (Safari, Chrome)

Same domain request:
  • 200 response: xhr = {status: 200, statusText: ‘OK’}
  • 304 response: xhr = {status: 304, statusText: ‘Not Modified’}
Cross domain request with proper headers:
  • 200 response: xhr = {status: 200, statusText: ‘OK’}
  • 304 response: xhr = {status: 304, statusText: ‘Not Modified’}
Cross domain request without proper headers:
  • 200 response: xhr = {status: 0, statusText: ”}
  • 304 response: xhr = {status: 0, statusText: ”}

IE

Same domain requests made with XMLHttpRequest, cross domain requests made with XDomainRequest.

Same domain request:
  • 200 response: xhr = {status: 200, statusText: ‘OK’}
  • 304 response: xhr = {status: 304, statusText: ‘Not Modified’}
Cross domain request with proper headers:
  • 200 response: onload invoked
  • 304 response: onload with cache, onerror otherwise
Cross domain request without proper headers:
  • 200 response: onerror invoked
  • 304 response: onerror invoked

Where’s the knot?

The problem with this is the lack of error handling. Firefox will return status 0 on all errors, but will indicate what the response was with statusText. It will also return 0 on a 304 response, which is a bit weird. Webkit on the other hand only returns status 0 on failed requests, but without telling you what’s wrong. You get the same error when a preflight request fails, a 500 error on the server or the real request fails. IE with its own separate XDomainRequest implementation simplifies this two handlers – onload and onerror. Unfortunately it raises an onerror request when a 304 is returned on empty cache.

jQuery issues

If you’re working with jQuery (and possibly other libraries) you’re in for a treat. Opera 9.5 falsely reports status = 0 when a HTTP status 304 is returned from the server. For this reason jQuery treats status === 0 as a successful request. Thus you have no way of knowing whether your request was a success or a failure.

Untying the knot

If you intend to use jQuery’s built-in methods you’ll need to do your own bookkeeping:

  1. Know when a request is a CORS request
  2. Wait for complete event
  3. If data is empty trigger error handling

For this to work you obviously always need some data. If empty data is valid you’re screwed.

Easy way out

You might have noticed before that I made a jQuery.windowName plugin. It still has the same name, but now also supports CORS requests and does some of the CORS related bookkeeping. As the latest version is still in testing you can get it directly at http://friedcell.net/js/jQuery.windowName/jQuery.windowName.plugin.1.0.0.js (test page).

Enhanced by Zemanta

One Response to “The Cross-Origin Request Sharing (CORS) knot”

  1. mouvedia says:

    I think you might be interested in https://github.com/Mouvedia/cb-fetch
    It does exactly what you prescribed.

Leave a Reply