Sunday, 3 February 2013

Cross-Origin Resource Sharing with Mule, AJAX and JavaScript

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: Response headers:

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

Simple HTTP Mule Flow

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.

Mule AJAX Flow

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

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

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

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!

18 comments:

  1. Hi,

    I've tried your example in Mule 3.3 and Mule 3.4 and I can not make it run since I get an error message in the flow, in the line of



    that says:

    Element: property is not allowed to be child of element Ajax.

    Any solution for this? Thank you very much.

    ReplyDelete
    Replies
    1. The line where I get the error didn't show up in my original message. It is the line where, using the spring:property element, we set jetty.xml as a new configuration

      Delete
    2. Hi Jose, MuleStudio will complain about the element =, but it should still run fine. Can you try running it? If you get an exception post it back here and i'll take a look. Thanks.

      Delete
    3. It will be helpful if you provide the github code of entire scenario

      Delete
    4. There is no GIthub code. just the gists above.

      Delete
    5. Hello There,

      Love it absolutely! So crystalline. No mumbo jumbo. No non-sense. Straight and simple. You guys need a standing ovation for your
      good work.


      Enterprises are adopting more agile, cost-effective, business technologies to keep pace with rapidly-changing expectations and requirements. According to Gartner more than 60% of midsize and large organizations will deploy Managed File Transfer (MFT) technologies to support a growing number of digital business.

      But nice Article Mate! Great Information! Keep up the good work!
      MuchasGracias,

      Delete
  2. Yeah, I am also facing same issue like in xml file.


    Please help me to figure out this.

    Could you provide the github code... So that i can clarify.

    ReplyDelete
    Replies
    1. Sain Bainuu,


      10/10 !!! Thank you for making your blogs an embodiment of perfection and simplicity. You make everything so easy to follow.


      With the upcoming Mule 4 release, we’re working to deliver a simpler runtime, one that’s easier to use and run for all organizations. Today, we’re excited to announce the next step in that process: the Mule 4 Release Candidate! This release expands on the simplicity improvements in the Mule 4 Beta, adds new capabilities, and incorporates the great feedback from the beta.





      But nice Article Mate! Great Information! Keep up the good work!


      Thanks,

      Delete
  3. It will run fine. It's just studio's validator complaining which you can't do anything about. If you run the application it will run fine.

    ReplyDelete
    Replies
    1. Hello Ryan I am getting deployment failed .I have tried which u mentioned above.And one more Where to write the JQuery Client code.Could you please replay me as I am waiting your replay.

      Thank you

      Delete
    2. what error are you getting? and can you share your code?

      Delete
    3. hello Ryan

      ERROR 2016-09-23 15:53:24,483 [main] org.mule.module.launcher.application.DefaultMuleApplication: null
      java.lang.ClassNotFoundException: Cannot load class 'org.mortbay.jetty.Server'
      at org.mule.module.launcher.application.CompositeApplicationClassLoader.loadClass(CompositeApplicationClassLoader.java:74) ~[mule-module-launcher-3.8.0.jar:3.8.0]
      at org.eclipse.jetty.util.Loader.loadClass(Loader.java:100) ~[jetty-util-9.0.7.v20131107.jar:9.0.7.v20131107]
      at org.eclipse.jetty.xml.XmlConfiguration$JettyXmlConfiguration.nodeClass(XmlConfiguration.java:364) ~[jetty-xml-9.0.7.v20131107.jar:9.0.7.v20131107]
      at org.eclipse.jetty.xml.XmlConfiguration$JettyXmlConfiguration.configure(XmlConfiguration.java:292) ~[jetty-xml-9.0.7.v20131107.jar:9.0.7.v20131107]
      at org.eclipse.jetty.xml.XmlConfiguration.configure(XmlConfiguration.java:248) ~[jetty-xml-9.0.7.v20131107.jar:9.0.7.v20131107]
      at org.mule.transport.servlet.jetty.JettyHttpConnector.initialiseFromConfigFile(JettyHttpConnector.java:273) ~[mule-transport-jetty-3.8.0.jar:3.8.0]
      at org.mule.transport.servlet.jetty.JettyHttpConnector.doInitialise(JettyHttpConnector.java:207) ~[mule-transport-jetty-3.8.0.jar:3.8.0]
      at org.mule.transport.servlet.jetty.JettyHttpsConnector.doInitialise(JettyHttpsConnector.java:52) ~[mule-transport-jetty-3.8.0.jar:3.8.0]
      at org.mule.transport.ajax.embedded.AjaxConnector.doInitialise(AjaxConnector.java:174) ~[mule-transport-ajax-3.8.0.jar:3.8.0]
      at org.mule.transport.AbstractConnector$1.onTransition(AbstractConnector.java:401) ~[mule-core-3.8.0.jar:3.8.0]
      at org.mule.transport.AbstractConnector$1.onTransition(AbstractConnector.java:365) ~[mule-core-3.8.0.jar:3.8.0]
      at org.mule.lifecycle.AbstractLifecycleManager.invokePhase(AbstractLifecycleManager.java:138) ~[mule-core-3.8.0.jar:3.8.0]
      at org.mule.transport.ConnectorLifecycleManager.fireInitialisePhase(ConnectorLifecycleManager.java:44) ~[mule-core-3.8.0.jar:3.8.0]
      at org.mule.transport.AbstractConnector.initialise(AbstractConnector.java:364) ~[mule-core-3.8.0.jar:3.8.0]
      at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:1.8.0_91]
      at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) ~[?:1.8.0_91]
      at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) ~[?:1.8.0_91]
      at java.lang.reflect.Method.invoke(Unknown Source) ~[?:1.8.0_91]


      My Mule XML Configuration file:




























      Delete
    4. You are using Mule 3.8 and a much newer version of the connector and jetty so org.mortbay.jetty.Server in the config file is no longer the correct class. You'll have to look into how to do this with the newer version. This post is old and for an old version of mule and Jetty. sorry.

      Delete
  4. Hi,

    I have deployed my mule code in server with some http://chandra:8080/getBookDetails

    And I am trying to hit that using the below snippet of nodejs

    try {
    superagent.get("http://chandra:8080/getBookDetails")
    .set("bookid", "534ght")

    }

    Ideally i am invoking get service but when i debug mule flow the request method is "OPTIONS" and all my header info is coming in acc-control-request-headers instead of direct headers, can you please let me know what exactly I need to do to get them in normal headers and the method should be get

    ReplyDelete
  5. Hi There,

    I love all the posts, I really enjoyed.
    I would like more information about this, because it is very nice., Thanks for sharing.


    I wrote about Kerberos support in MuleSoft, so it seems only natural to discuss the other place where Kerberos fits into the API ecosystem. Not only can Kerberos be used to secure APIs hosted or proxied by MuleSoft, but certain third-party systems can use or even require Kerberos for their security.



    By the way do you have any YouTube videos, would love to watch it. I would like to connect you on LinkedIn, great to have experts like you in my connection (In case, if you don’t have any issues).
    Please keep providing such valuable information.



    Regards,

    Krishna

    ReplyDelete
  6. Greetings Mate,

    Allow me to show my gratitude bloggers. You guys are like unicorns. Never seen but always spreading magic. Your content is yummy. So satisfied.
    I started using Mulesoft Training USA blog for my training practice.

    At MuleSoft, we work with a number of hospitals, healthcare systems, insurers, and other healthcare organizations. These organizations use different computer systems–from billing and Electronic Health Records (EHRs) to laboratory and pharmaceutical management systems.
    A common and critical use case that we come across is how we can enable these organizations and their partners to seamlessly exchange data with one another across different systems.

    Once again thanks for your tutorial.


    Ciao,
    Morgan

    ReplyDelete
  7. • Nice and good article. It is very useful for me to learn and understand easily. Thanks for sharing your valuable information and time. Please keep updating mulesoft Online Training Hyderabad

    ReplyDelete