Fork me on GitHub

OpenLink Software logo

64px OSDE icon
OSDE
The OpenLink Structured Data Editor

     

Intro

When a browser loads a page from a server, which perform an Ajax call via javascript to a second server, the browser will setup an HTTP connection to the second server, add an —

Origin: host

— header, perform the OPTIONS/GET/HEAD/PUT request, and retrieve the result-headers as well as any data passed back from the second server.

The browser then checks if the result-headers contain a —

Access-Control-Allow-Origin: host

— or —

Access-Control-Allow-Origin: *

If this header is available, the browser will set the correct HTTP status code, as well the responseText, which is needed to make a proper error dialog if the status is an error state like 404.

If this header is not available, the browser will simply return a HTTP status code 0 and a status text "error", no matter whether the request succeeded or failed to begin with.

The console log will contain get a more informative CORS related error, but this is NOT available for normal javascript to retrieve.

It is also impossible, as far as we have been able to determine, to get the response headers from the server (which is the only place from which the real HTTP status codes and other texts can be retrieved).

So when an Ajax call gets 'error' with HTTP status 0, the ONLY dialog we can give is that it possibly is a CORS related problem.

The only reason we can think of why browser developers would have coded it like this, would be to hide URL probing, so when a server is NOT CORS enabled, no-one can try out different URLs to see whether they are there (which would be indicated by the state 0) or not (which would result in a proper 404 status). However, since it would be just as easy to test this directly with curl, it is rather a far-fetched reason.

We cannot find an official answer for this strange behavior, but it does makes web app development and error reporting very hard.

Configuration

Most web server installations do not return CORS headers on HTTP 40x error states, which means that any error from these servers is simply NOT retrievable.

It took us a long time to actually get both apache and nginx to add these headers to 40x states, which is something of course NOT fully documented correctly by the CORS proponents.

There are a number of different scenarios that have different effects when a page with javascript tries to make an Ajax call to retrieve some data.

Scenario 1

We have a server that is not CORS enabled, but the app is running on the same server.

In this case the app works without any problems, as the browser sees the call is to the same host that the page was loaded from, so it will not use Origin and will return the proper HTTP status codes and error messages.

Scenario 2

We have a server that is not CORS enabled, and the app is running from a different host.

In this case all Ajax requests will check for Access-Control-Allow-Origin in the response-headers, and because it is not there, all requests get HTTP code 0 and should report something like 'Request failed possibly due to missing CORS setup of the server'.

Scenario 3

We have a server that is CORS enabled for normal pages (where the header is returned by the page), and the app is running from a different host.

In this case any page request that succeeds with status 200 will work, but any redirect (30x) or error (40x, 50x) will NOT return a proper error, since these pages will not have an Access-Control-Allow-Origin, so the error state is completely hidden from the web application and can therefore only report something like 'Request failed possibly due to missing CORS setup of the server'.

Scenario 4

We have a server that is CORS enabled, and an app running on the SAME system. However the app calls a short URL on the same server, so this does not require any special headers.

Unfortunately that short URL is automatically expanded by the Ajax call which will do an internal retry to the new Location which may be on another server. In this case if that new server conforms to scenario 1, 2, or 3 you still won't get any error information other than an HTTP status 0. What is even worse is that you cannot even get the info to the redirected URL so you cannot even check yourself if the url.location.host matches windows.location.host which is the way the rdf -editor tries to distinguish CORS errors from non-CORS errors when HTTP state = 0.

Scenario 5

We have a server that is CORS enabled for ALL pages, including redirect (30x) and error (40x, 50x) pages, and an app is running from a different host.

In this case there will always be an Access-Control-Allow-Origin in the response-headers and error information IS passed on to the web application to show to the user.

Currently, http://dbpedia.org is the ONLY system that has this complete setup enabled.

Example

$ curl  -I -H 'Origin: pipo' "http://dbpedia-live.openlinksw.com/resource/OpenLink"
HTTP/1.1 303 See Other
Date: Thu, 07 Apr 2016 19:17:07 GMT
Content-Type: text/html; charset=UTF-8
Content-Length: 0
Connection: keep-alive
Server: Virtuoso/07.20.3216 (Linux) i686-generic-linux-glibc212-64  VDB
Location: http://dbpedia-live.openlinksw.com/page/OpenLink
Expires: Thu, 07 Apr 2016 19:18:07 GMT
Cache-Control: max-age=60

As we see the redirect page does not have the correct CORS headers, so this next link will give a generic CORS error:

http://ods-qa.openlinksw.com/rdf-editor/#/editor?uri=http:%2F%2Fdbpedia.openlinksw.com2Fresource%2FOpenLink

$ curl -I -H 'Origin: pipo' "http://dbpedia.org/resource/OpenLink"
HTTP/1.1 303 See Other
Date: Thu, 07 Apr 2016 19:17:27 GMT
Content-Type: text/html; charset=UTF-8
Content-Length: 0
Connection: keep-alive
Server: Virtuoso/07.20.3216 (Linux) i686-generic-linux-glibc212-64  VDB
Location: http://dbpedia.org/page/OpenLink
Expires: Thu, 14 Apr 2016 19:17:27 GMT
Cache-Control: max-age=604800
Access-Control-Allow-Origin: pipo
Access-Control-Allow-Credentials: true
Access-Control-Allow-Methods: HEAD, GET, POST, OPTIONS
Access-Control-Allow-Headers: DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type

Since the 303 page does have the right CORS headers, the Ajax call will subsequently load the correct .n3 page and display the results:

http://ods-qa.openlinksw.com/rdf-editor/#/editor?uri=http:%2F%2Fdbpedia.org%2Fresource%2FOpenLink