CORS Filter

Tips and Tricks

How to detect CORS support in a browser

The following JavaScript snipped can be used to detect CORS support in the browser:

function browserSupportsCors() {
	if ("withCredentials" in new XMLHttpRequest())
		return true;
	else if (typeof XDomainRequest == "object")
		return true;
	else
		return false;
}

The XDomainRequest object is specific to IE 8+, the other browsers extend the existing XMLHttpRequest.

For IE 8, 9: Keep all your responses as text/plain

If you want your cross-domain web service to support as many browsers as possible set the content-type of all your responses to text/plain.

Content-Type: text/plain

Internet Explorer's CORS implementation, via the XDomainRequest object, imposes a number of limitations on cross-domain calls, including a rule that only "text/plain" responses are accepted. If your service returns any other content type, e.g. "application/json", the response will be silently discarded.

Check out the following post for more information:

XDomainRequest - Restrictions, Limitations and Workarounds

Update February 2012: Good news for developers! Internet Explorer 10 is expected to bring full CORS support through the XMLHttpRequest object as other browsers do. Read the MSDN article.

CORS and browser cookies

Users occasionally report that browser cookies don't work as expected in a cross-domain setting.

Does your filter take care of sessions? For each CORS request I get a different JSESSIONID.

In order for a Java web application or service to get at a cookie, both the CORS Filter in front of it as well as the requesting JavaScript must explicitly allow this form of credential:

First, the JavaScript that initiates the XHR call must set its "withCredentials" flag to true. If not the browser will prevent the passing of cookies during the cross-domain call.

var xhr = new XMLHttpRequest();
var url = 'http://bar.other/resources/credentialed-content/';

xhr.open('GET', url, true);
xhr.withCredentials = true;
xhr.onreadystatechange = handler;
xhr.send();

Second, the CORS Filter must also expressly state to the browser that cookies are permitted. This is advertised during the so called "pre-flight" request that the browser makes before an actual request that may involve credentials, such as cookies. The CORS Filter ships with a default configuration where credentials, such as cookies, are marked as allowed when responding to preflight requests. To restrict this edit the cors.supportsCredentials configuration parameter.

The Java CORS filter itself doesn't access the cookie headers in any way, nor does it interface to the JSESSIONID.

So, to sum up, both the calling script on the browser side as well as the CORS Filter on the server side must expressly have the credentials flag enabled for cookies to pass through.

There is a snag however and it has to do with Internet Explorer. Its XDomainRequest implementation of CORS doesn't allow cookies to be passed at all, for security reasons says Microsoft. Which is probably a good thing. So if you wish to achieve wider browser support for your cross-domain application or service you will have to use an alternative mean to cookies for storing session IDs, such as passing the token as an URL parameter.

Allowing HTTP basic authentication

If your web server requires HTTP basic authentication to process requests you need to tell CORS aware browsers to let Authorization headers through, else the browser will not send them and authentication will fail.

To do that add the Authorization header name to the list of supported headers (cors.supportedHeaders):

Authorization

Allowing subdomains

If you wish to allow CORS requests to multiple subdomains in your domain, but for various reasons cannot list them in the filter configuration, set the cors.allowSubdomains parameter to true.

For example, if the explicitly allowed origin is http://example.com and the subdomains setting is on, then all of the following subdomain origins will be automatically allowed too:

http://www.example.com
http://webmail.example.com
http://sso.example.com
http://blog.example.com

Note that for a subdomain to be allowed, both its scheme (http or https) and its suffix (top domain host name + optional IP address) must match.

This feature was contributed in version 1.4.

HTTP caching / CDN support

CORS Filter 1.9.2 added support for the Vary header to prevent erroneous caching of CORS response headers.

Read the full discussion is at https://bitbucket.org/thetransactioncompany/cors-filter/issue/16.

Asynchronous servlet processing

The Servlet 3.0 specification added support for asynchronous processing of requests which can greatly increase the throughput of Java web applications.

Asynchronuous processing requires the following line to the added to the CORS Filter declaration in the web.xml descriptor:

<filter>
	<filter-name>CORS</filter-name>
	<filter-class>com.thetransactioncompany.cors.CORSFilter</filter-class>
	<async-supported>true</async-supported>
</filter>

Automatic reconfiguration

A special variant of the CORS Filter is available which can automatically detect changes to the configuration file and reconfigure itself.

Stick the following declaration into your web.xml file to use CORS Filter variant:

<filter>
	<filter-name>CORS</filter-name>
	<filter-class>com.thetransactioncompany.cors.autoreconf.AutoReconfigurableCORSFilter</filter-class>
</filter>

This filter variant must be configured with an external Java properties file. The filter init-param style configuration will not work here as the web.xml file may not be modified at runtime.

The configuration file will be polled for changes every 20 seconds. If a change is detected the filter will automatically reload itself with the new configuration. If the new configuration is invalid an error message will be printed to the server log and the filter will continue operating with its previous intact settings.

To override this interval set the cors.configFilePollInterval system property.

For example, to set the poll interval to 5 seconds:

-Dcors.configFilePollInterval=5

Spring Bean configuration

Somewhere in your Spring configuration files, you need to initialise the bean similar to this:

<bean id="corsFilter" class="com.myproject.filters.CorsSpringFilter" >
    <constructor-arg >
        <bean class="com.thetransactioncompany.cors.CORSConfiguration" >
	    <constructor-arg>
	        <!-- Init the CORS filter via Spring so that properties can be extracted from anywhere-->
		<props>
                    <prop key="cors.allowOrigin">${cors.allowOrigin}</prop>
                    <prop key="cors.allowSubdomains">${cors.allowSubdomains}</prop>
                    <prop key="cors.supportedHeaders">${cors.supportedHeaders}</prop>
                    <prop key="cors.exposedHeaders">${cors.exposedHeaders}</prop>
                    <prop key="cors.supportedMethods">${cors.supportedMethods}</prop>
                    <prop key="cors.supportsCredentials">${cors.supportsCredentials}</prop>
                    <prop key="cors.maxAge">${cors.maxAge}</prop>
                    <prop key="cors.tagRequests">${cors.tagRequests}</prop>
                </props>
            </constructor-arg>
        </bean>
    </constructor-arg>
</bean>

A Spring user still needs to provide a wrapping class along the lines of

@Component
public class CorsSpringFilter implements javax.servlet.Filter {
    static CORSFilter corsFilter;

    public CorsSpringFilter(CORSConfiguration corsConfiguration) throws ServletException {
        corsFilter = new CORSFilter(corsConfiguration);
    }

    public CorsSpringFilter() {
        super();
    }

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        corsFilter.doFilter(request, response, chain);
    }

    @Override
    public void destroy() {
        corsFilter.destroy();
    }
}

Which can be configured for deployment in web.xml thus:

<filter>
    <filter-name>corsFilter</filter-name>
    <filter-class>CorsSpringFilter</filter-class>
</filter>

FireBug is your friend

The versatile FireBug add-on is an excellent mean for debugging cross-domain calls. Yes, that's another reason to get FireFox :-)

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.

Firebug XHR
How to load the Firebug Network Monitor

For a simple/actual CORS request Firebug will then display the following headers:

Simple request
Debugging a simple/actual CORS request

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.

Simple request
Debugging a simple CORS response

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.

How to test CORS requests with a single web server

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.