Enterprise Integration Zone is brought to you in partnership with:

Ryan Carter is a Solution Architect and Author specializing in integration and APIs. Ryan is passionate about open source and is an appointed Mule champion regularly contributing to the community. Ryan is a DZone MVB and is not an employee of DZone and has posted 5 posts at DZone. You can read more from them at their website. View Full User Profile

Cross-Origin Resource Sharing with Mule, AJAX and JavaScript

02.12.2013
| 3783 views |
  • submit to reddit

The Same-Origin Policy

The Same-Origin policy is a security policy enforced on client-side web apps to prevent interactions between resources from different origins. While useful for preventing malicious behaviour such as XSS(Cross Site Scripting) attacks, this security measure also prevents useful and legitimate interactions between known origins.

For example, your new awesome JavaScript mashup hosted at http://myawesomeapp.com might want to use a REST API hosted at myawesomeapi.cloudhub.io. However, because these are two different origins from the perspective of the browser, the browser won't allow a script from myawesomeapp.com to fetch resources from myawesomeapi.cloudhub.io because the resource being fetched is from a different origin.

Cross-Origin Resource Sharing

Fortunately, there is a solution via Cross-Origin Resource Sharing(CORS). The CORS spec was developed by the World Wide Web Consortium (W3C) to support this very case. It's a working draft but is already supported by the majority of web browsers, probably including the very browser you are using to view this page. The full specification can be found at: http://www.w3.org/TR/access-control/ and supported browsers can be found here: http://caniuse.com/cors.

How CORS works

CORS works via a set of HTTP headers in the request from the client app and the response from the requested resource. In it's simplest form; the requesting application specifies a Origin header in the request which describes the origin of the request and the requested resource will reply intern with an Access-Contol-Allow-Origin header indicating specific origins that are allowed to access a particular resource.

Request headers:

GET /awesomeapi/list HTTP/1.1
Host: myawesomeapp.com
User-Agent: Mozilla/5.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Connection: keep-alive
Origin: http://myawesomeapp.com

Response headers:
HTTP/1.1 200 OK
Access-Control-Allow-Origin: http://myapp.com
Content-Type: application/json; charset=utf-8

There are more complicated scenarios that require additional HTTP headers when using non-simple HTTP headers. More information on this can be found here: https://developer.mozilla.org/en/docs/HTTP_access_control#Access-Control-Allow-Methods. For the purposes of this post we will just be using simple headers.

Using CORS with the Mule HTTP transport

To demonstrate CORS in action, I'll show a simple JavsScript client app using JQuery to access a simple HTTP service in Mule.

Simple JQuery Client

<html>
<head>
 
<title>Mule CometD JQuery Client</title>
 
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
 
<script
  src="http://ajax.googleapis.com/ajax/libs/jquery/1.8/jquery.min.js"></script>
 
</head>
<body>
<pre>Log output</pre>
<script>
 $.ajax({
  	type : "GET",
		dataType : "json",
		url : "http://localhost:8082/myawesomeapi",
		complete : function(jqXHR, textStatus) {
		    $("body").append(
          "<pre>" + $.parseJSON(jqXHR.responseText) + "</pre>");
		}
	});
</script>
</body>
</html>

Simple HTTP Mule Flow
<mule xmlns:pattern="http://www.mulesoft.org/schema/mule/pattern"
  xmlns="http://www.mulesoft.org/schema/mule/core" xmlns:ajax="http://www.mulesoft.org/schema/mule/ajax"
  xmlns:http="http://www.mulesoft.org/schema/mule/http"
	xmlns:doc="http://www.mulesoft.org/schema/mule/documentation"
	xmlns:spring="http://www.springframework.org/schema/beans" xmlns:core="http://www.mulesoft.org/schema/mule/core"
	version="EE-3.3.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="
		http://www.mulesoft.org/schema/mule/pattern http://www.mulesoft.org/schema/mule/pattern/current/mule-pattern.xsd 
		http://www.mulesoft.org/schema/mule/ajax http://www.mulesoft.org/schema/mule/ajax/current/mule-ajax.xsd 
		http://www.mulesoft.org/schema/mule/http http://www.mulesoft.org/schema/mule/http/current/mule-http.xsd 
		http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-current.xsd 
		http://www.mulesoft.org/schema/mule/core http://www.mulesoft.org/schema/mule/core/current/mule.xsd">
    
	<flow name="httpCORS" doc:name="httpCORS">
		<http:inbound-endpoint address="http://localhost:8081/myawesomapi" />
 	
 		<set-payload value="Hello World" />
 		
		<set-property name="Access-Control-Allow-Origin" value="*" />
	</flow>
</mule>

This is just a simple JQuery client and a simple HTTP Mule flow returning some plain text: "Hello World". The most important part here is the set-property element. Here we are setting the HTTP header to be returned in the response. Simple right? We have just set the value to "*" indicating that any origin is allowed. This can be configured as needed to include specific origins if you so desire. Using CORS with the Mule AJAX transport

On top of the HTTP transport in Mule, there is also a specific AJAX transport. The Mule AJAX transport allows Mule events to be sent and received asynchronously to and from the web browser.

You might think that you would be able to se this property the same way. Unfortunately, no. Under the hood; the AJAX transport uses Jetty and the CometD libraries to provide the long-polling functionality and currently do not propagate HTTP headers set in Mule and instead set their own.

Never fear, there is a solution. It's a little more long winded, but still simple none the less. The solution relies on Jetty's configuration, which is used by the AJAX transport when running in embedded mode. This configuration can be overrided within your Mule application by provided a custom Jetty XML configuration file and creating custom Handler to add new HTTP headers.

Simple JQuery CometD client

To start let's amend the original client application to use CometD to subscribe to a channel in Mule.


<html>
<head>

<title>Mule CometD JQuery Client</title>

<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />

<script
  src="http://ajax.googleapis.com/ajax/libs/jquery/1.8/jquery.min.js"></script>
<script src="http://ajax.cdnjs.com/ajax/libs/json2/20110223/json2.js"></script>
<script
  src="http://jquerycomet.googlecode.com/svn/trunk/jquery.comet.js"></script>

</head>
<body>
<pre>Log output</pre>
<script>
	$.comet.init("http://localhost:8083/ajax/cometd");
	$.comet.subscribe("/mule/channel", receive);

	function receive(message) {
		$("body").append(
				"<pre>" + message.channel + " : " + message.data + "</pre>");
	}
</script>
</body>
</html>

Mule AJAX Flow

The Mule flow just polls every ten seconds and publishes a message to an AJAX outbound endpoint.

<?xml version="1.0" encoding="UTF-8"?>

<mule xmlns:pattern="http://www.mulesoft.org/schema/mule/pattern"
  xmlns="http://www.mulesoft.org/schema/mule/core" xmlns:ajax="http://www.mulesoft.org/schema/mule/ajax"
	xmlns:http="http://www.mulesoft.org/schema/mule/http"
	xmlns:doc="http://www.mulesoft.org/schema/mule/documentation"
	xmlns:spring="http://www.springframework.org/schema/beans" xmlns:core="http://www.mulesoft.org/schema/mule/core"
	version="EE-3.3.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="
		http://www.mulesoft.org/schema/mule/pattern http://www.mulesoft.org/schema/mule/pattern/current/mule-pattern.xsd 
		http://www.mulesoft.org/schema/mule/ajax http://www.mulesoft.org/schema/mule/ajax/current/mule-ajax.xsd 
		http://www.mulesoft.org/schema/mule/http http://www.mulesoft.org/schema/mule/http/current/mule-http.xsd  
		http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-current.xsd 
		http://www.mulesoft.org/schema/mule/core http://www.mulesoft.org/schema/mule/core/current/mule.xsd">
	<jms:activemq-connector name="activeMQ-Embedded"
		specification="1.1" doc:name="Active MQ" />

	<ajax:connector name="ajaxServer" serverUrl="http://0.0.0.0:8083"
		resourceBase="${app.home}/docroot" doc:name="Ajax">
		<spring:property name="configFile" value="jetty.xml" />
	</ajax:connector>

	<flow name="dynamicJmsBridge" doc:name="dynamicJmsBridge">
		<poll>
			<set-payload value="Hello World" />
		</poll>

		<ajax:outbound-endpoint channel="/mule/channel"
			connector-ref="ajaxServer" doc:name="Ajax" />
	</flow>


	<flow name="financialNewsValueGenerator" doc:name="financialNewsValueGenerator">
		<poll frequency="3000">
			<set-payload value="Chelsea 5 - MUFC 0" doc:name="Set Payload" />
		</poll>

		<jms:outbound-endpoint connector-ref="activeMQ-Embedded"
			queue="public.financial.news" doc:name="JMS" />
	</flow>
</mule>

In addition to the standard AJAX connector configuration, we are injecting a reference to a custom jetty configuration file to register our CORS handler.

Jetty Configuration
<?xml version="1.0"?>

<!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN" "http://jetty.mortbay.org/configure.dtd">



<!-- =============================================================== -->

<!-- Configure the Jetty Server                                      -->

<!--                                                                 -->

<!-- Documentation of this file format can be found at:              -->

<!-- http://docs.codehaus.org/display/JETTY/jetty.xml                -->

<!--                                                                 -->

<!-- =============================================================== -->





<Configure id="Server" class="org.mortbay.jetty.Server">



    <Set name="handler">

      <New id="Handlers" class="org.mortbay.jetty.handler.HandlerCollection">

        <Set name="handlers">

         <Array type="org.mortbay.jetty.Handler">

           <Item>

             <New id="Contexts" class="org.mortbay.jetty.handler.ContextHandlerCollection"/>

           </Item>

           <Item>

             <New id="CORSHandler" class="org.oreilly.mulecloudconnect.CORSHandler"/>

           </Item>

         </Array>

        </Set>

      </New>

    </Set>



</Configure>

This is just a simple jetty configuration file that we referenced in the previous Mule configuration to register our new custom Handler. The most important part here is the class reference that will be our new Handler to add the required headers: org.oreilly.mulecloudconnect.CORSHandler

Custom CORS Handler
package org.oreilly.mulecloudconnect;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.mortbay.jetty.handler.AbstractHandler;

public class CORSHandler extends AbstractHandler {
    @Override
    public void handle(String target, HttpServletRequest request, HttpServletResponse response, int dispatch) throws IOException, ServletException {
      response.addHeader("Access-Control-Allow-0rigin", "*");
    }
}

And finally the last part to our CORS puzzle is the custom Handler itself. This class is an extension of the org.mortbay.jetty.handler.AbstractHandler class that gives us access to the Servlet request and response. In this example we are simply adding the Access-Control-Allow-Origin header to the HttpServletResponse. But again, you can customize this to add specific origins and so on.

And that's it. Happy mashing!






 

Published at DZone with permission of Ryan Carter, author and DZone MVB. (source)

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)