CORS
Cross Origin Request headers are a mechanism that enables cross domain XHRRequests to access content in the response.
It is implemented in most browsers.
Further details here: https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS
CORS support
Rubris’ support for CORS has the following rules on how it adds CORS headers to responses:
- A request with no “origin” header is always considered a non cors request.
- A request with an “origin” header that is not listed as an accepted origin is considered a non-cors request
- Static resources in the application are considered non-origin requests
Therefore CORS headers are added to
- All XHRRequest (or similar) with “origin” headers for non-static services, such as the EngineIO protocol path, Direct Paths, the service JSON call
- Errors that occur for these requests (except access denied based on the connection deny handler)
The Headers
By default Rubris is configured to set:
Access-Control-Allow-Methods: GET, POST
Access-Control-Allow-Origin: <BROWSER_ORIGIN_HEADER_FROM_REQUEST>
Access-Control-Allow-Credentials: true
Access-Control-Max-Age: 86000
Access-Control-Allow-Headers: Content-Type, Authorization,
X-Authorization, X-Requested-With, Location
To replace the defaults, the values can be adjusted in the HTTPConfig:
HttpConfig config = ((HttpConfig) s.getConfig().getTransportConfig( ModuleConfig.HTTP)); // set the headers returned in the allowed set config.setCustomCORSHeaders(new LinkedHashSet(Arrays.asList( new String[]{"header1","header2"}))) // Change the methods allowed config.setCustomCORSMethods(new LinkedHashSet(Arrays.asList( new String[]{"HEAD","POST"}))); // set credentials to false config.setAllowCorsCredentials(false);
You cannot override the Access-Control-Allow-Origin to remove it or set it to be *.
Nor can the CORS functionality be disabled.
OPTIONS Requests and POSTS
Current browsers are restrictive in that the CORS OPTIONS request is always made if the content type is not a text format text/plain etc.
This means that Cross-Origin POSTS for the binary format will ALWAYS result in an OPTIONS call prior to each POST that is binary (or any other type that is not text/plain for that matter). The poll for the heartbeat does not exhibit this behaviour as the outgoing type is text/plain.
The GET requests that act as the long poll do not require an OPTIONS call.
Normally the POST ratio is low for this sort of application as the push is infrequent. However, it does add some overhead for the extra check for the outbound path for POSTS to the server.
However, this can cause issues for both proxy servers and the additional round-trip for each post pushing the times out. also some applications that are POST heavy will suffer from an extra round-trip for every call.
Rubris supports a workaround to prevent the OPTIONS request being made. To enable this the Content-Type must be set to text/plain:charset=rubris-cors-hack
(exactly this string with no spaces or case changes) on the outgoing POSTS only that would normally be set to application/octet-stream
.
Engine-IO sets this value by default so there 2 approaches to this.
- Change the Engine-IO library
- MonkeyPatch the XMLHttpRequest object.
The latter is easier to achieve and a simple mechanism to do this is something like:
if (!XMLHttpRequest.prototype.oldSetRequestHeader){ XMLHttpRequest.prototype.oldSetRequestHeader = XMLHttpRequest.prototype.setRequestHeader; XMLHttpRequest.prototype.setRequestHeader = function(key,value) { if ('application/octet-stream' == value){ XMLHttpRequest.prototype.oldSetRequestHeader.call(this, key, 'text/plain;charset=rubris-cors-hack') }else{ XMLHttpRequest.prototype.oldSetRequestHeader.call(this, key,value); } }; }
Obviously this is quite crude and it is probably better to override only the requests that go to specific URLs or similar.
It is up to the surrounding code to implement this behaviour and the mechanism by which this is achieved is not important just that the content-type is swapped for the hack type. The server will automatically treat this as binary and process the POST as if it was application-octet-stream
.