CORS, W3C’s mechanism for standardised cross-domain XHR has reached a turning point. More than 50% of user browsers* support it now, with another 31% (MS IE 8 + 9) implementing the core functionality. This means that 81% of browsers allow at least basic execution of cross-origin XHR. Very good news, considering my ongoing effort to promote CORS as a replacement for JSONP and my the open-source CORS Servlet Filter for Java web apps which I developed in 2010 and continue to maintain.
From the major browser vendors, the only one that doesn’t have CORS support yet is Opera. But there have been leaks on the web that they will soon have it too. Firefox has had CORS since 3.5, Internet Explorer since 8.0 (partial, but enough for most purposes), Google Chrome since 9.0 and Safari since 3.2. I suppose that in about 12 months time CORS support will cross the 90% milestone, which will open up the majority of developers to switch to it with confidence.
As for NimbusDS, cross-domain requests have been supported since 2010 and all new products incorporate it. Next week I’m planning to launch a Json2Ldap demo on the CloudBees PaaS which makes exclusive use of CORS for the JSON requests to the remote cloud web service.
Firebug, the indispensable Firefox add-on for serious developers, is great for debugging web apps that make use of the new CORS mechanism for cross-domain requests. The acronym CORS stands for Cross-Origin Resource Sharing and was developed by a group of web activists to address the issue of making browser cross-domain requests in a standards-compliant way. The goal is to make CORS an official W3C standard. As of 2010 most browsers such as FF, IE, Chrome and Safari support it.
My first encounter with CORS was during the development of Json2Ldap, a smart piece of software designed to turn one or more LDAP directories into a web friendly JSON-RPC service. Json2Ldap turned out to be an excellent case for CORS as the web service had to be capable of serving cross-domain requests. Later I span off the server-side CORS code into a project on its own, called CORS Filter. It provides a generic servlet filter which can be retrofitted to any existing Java web app to make it cross-domain XHR capable.
Firebug has an excellent network activity monitor which can be used to reveal the CORS-specific conversation between browser and server during a cross-domain XHR. This is great for understanding how the protocol works, particularly the behaviour of simple/actual vs. pre-flight CORS requests.
To observe a CORS request in action you need a relatively recent version of Firebug (e.g. 1.6). Click on the “Net” tab and then on the “XHR” tab, then launch your cross-domain request.
For a simple/actual CORS request Firebug will then display the following headers:
Note the Origin header which the browser inserted to identify the domain from which the HTML page originated. The server checks the Origin value and responds accordingly, by either allowing or denying the request.
If the server allowed the origin it replies with an Access-Control-Allow-Origin header that contains a verbatim copy of the Origin header value or * (meaning any origin). The server may also include optional CORS response headers, such as Access-Control-Allow-Credentials (to tell the browser that it accepts cookies) or Access-Control-Expose-Headers (to tell the browser which custom headers are safe to expose to the requesting script).
CORS also has the so-called preflight request which browsers can send using HTTP OPTIONS prior to the actual CORS request to query the CORS settings of a particular HTTP server, such as the supported HTTP methods, custom headers, etc.
Tip: How to simulate a cross-domain request if you have just a single HTTP server to play with: Create a host alias of your web server and reload the requesting page using the alias URL.
Finally, today I learned that the Jetty 7 web server has a CORS filter implementation similar to the one I developed few months ago. On first inspection the only notable difference is that it doesn’t support request tagging and the Access-Control-Expose-Headers response header.
October saw a release of Json2Ldap version 1.6 which brings updates in three areas: the web API, addition of CORS support and library upgrades. The software is now being put to serious use and I’m glad to see its popularity pick up, despite having no dedicated marketing effort behind it. I must say the whole concept of web-enabling LDAP directories with a simple JSON-RPC API is one very attractive proposition for companies offering web services, such as social networking or SaaS, as a way to provide web-exposed user storage API and authentication in a standard and web friendly fashion.
Cross-Origin Resource Sharing (CORS)
Json2Ldap now includes support for the CORS protocol that allows browsers to make cross-origin XHR requests, in the spirit of modern Ajax/Web 2.0 applications where a web application uses data and methods from several sources. CORS is now supported by most mainstream browsers, such as Firefox, IE, Chrome and Safari. The protocol is implemented by a highly flexible and configurable servlet filter, which I also offer as a separate add-on product to customers who want to add CORS to their own Java web services.
Json2LDAP API changes
Json2Ldap 1.6 also makes some minor changes to the web API, in order to make it more generic and allow for future implementation of LDAP extensions, such as password expiration controls.
Connection identifiers (CIDs), returned by the ldap.connect call, are now represented by standard GUIDs (or UUIDs).
ldap.connect returns an object where the CID values is put into a separate field.
ldap.simpleBind and ldap.anonymousBind return empty object instead of null; this is to allow for new optional result fields in future without breaking backward compatibility.
The interactive shell for querying remote JSON-RPC 2.0 web services received a new feature in line with the recent release of CORS Filter 1.0 – there is now a command line option to set an “Origin” header to simulate cross-origin browser requests. For those of you who still don’t know, Cross-Origin Resource Sharing (CORS) is a recent W3C effort to introduce a standard mechanism for making cross-origin requests from browsers. CORS is now supported by Firefox 3.5+, IE 8, Safari 4+ and Chrome 3+.
The origin request header can be used by cross-site aware web servers to determine whether a particular HTTP request is CORS and what access policy to apply to it.
Here is an example “Origin” HTTP header which the browser inserts in cross-site XHR to indicate that the original web page/script is from http://example.com:
To set an “Origin” request header for a particular JsonRpc2 Shell session use the optional -o, --origin argument:
Last month I released CORS Filter, the first universal software solution for fitting Cross-Origin Resource Sharing (CORS) support to Java web applications. CORS is a recent W3C effort to introduce a standard mechanism for enabling cross-site requests in web browsers and servers.
Since the early days of the web (think Netscape 2.0) browsers have enforced, to various degrees, a same origin policy to prevent leaking of confidential user data to third party sites. The same origin policy was carried over to the game-changing XHR which appeared in the early 2000’s. Modern web applications, however, incresingly seek to dynamically integrate content and services from third parties, which is currently achieved through “hacks” such as JSONP. CORS was created in recognition that cross-domain requests are here to stay and therefore they’d better be standardised.
The philosophy of CORS
CORS works in two directions:
From a browser script perspective: By imposing tighter controls on data exchanged during a cross-site request. Cookies, for instance, are blocked unless specifically requested by the XHR caller and allowed by the remote web service. This is done to reduce the said risk of data leaks.
From a web service perspective: By requiring the browser to report the origin URL to the target cross-site service, so the latter can determine, based on its origin policy, whether to allow or deny the request.