Menu

Flash to the Rescue

June 28, 2006

Jason Levitt

It's still no fun to work around the cross-domain security restriction that web browsers impose on the XMLHttpRequest object (and IE's XMLHTTP ActiveX object), but with the advent of more interesting REST web services, Ajax developers are looking for ways to make web services requests directly from client web browsers, rather than loading down their servers.

The cross-domain request restrictions guideline, or Same Origin Policy, which has governed browser security since almost the beginning of recorded browser history (Netscape Navigator 2.0), has spawned a number of clumsy workarounds--many of which have probably been used by folks interested in simplifying their Ajax mashups and applications.

The usual workaround is to use server-based proxies, which I discuss here. For the more daring, there's the limited yet powerful Dynamic Script Tag approach. However, the latest entry in the field of hacks and workarounds has some potential advantages over all of these (See Table 1). Based on Adobe (nee Macromedia) Flash technology and written by Julien Couvreur, the FlashXMLHttpRequest proxy, currently in stable beta, is a small (less than 5K) Flash SWF file that can make HTTP GETs and POSTs on your Ajax application's behalf.

Table 1
Table 1. XMLHttpRequest compared to the Dynamic Script Tag (Click image for full-size screen shot).

FlashXMLHttpRequest has two primary advantages over other approaches. First, it's truly cross-platform since it works the same in any web browser with Flash versions 6, 7, or 8 installed (close to 95% of modern desktop web browsers). Second, it has a simple security model to allow cross-domain requests, which is already in use by many sites that offer web services, such as Yahoo and Amazon. The security is implemented on the server side by placing a single text file, named crossdomain.xml, in the root directory of the domain that hosts the web services. Rules in the file specify which domains or IP addresses are allowed to make web services requests. On the client side, cross-domain access is denied by default, but if the client finds a crossdomain.xml file on the server and the rules allow it to connect, a request can proceed. You can view examples of crossdomain.xml files for Amazon and Yahoo here and here, respectively.

From a security standpoint, another advantage is that any cookies that are returned to the browser as a result of the HTTP request aren't accessible from the SWF file or the supporting JavaScript, so security issues involving access to the client's cookies aren't available to malicious JavaScript scripts.

Using FlashXMLHttpRequest

FlashXMLHttpRequest consists of several SWF files, one for Flash 8 and two that support both Flash 6 and 7. JavaScript detects the version of Flash in the client browser and the appropriate SWF file is loaded. The Dojo JavaScript Toolkit provides the interface layer between your Ajax application and the Flash SWF files, and almost all of the JavaScript code overhead (at least 115K, the size of the dojo.js file) comes from the Dojo JavaScript libraries. (Work is under way to reduce the JavaScript code overhead, so future versions may be smaller.) To support JavaScript-to-Flash communication in Flash 6 and 7, Brad Neuberg created the DojoExternalInterface library to emulate the JavaScript-to-Flash communication interface in Flash 8 (called ExternalInterface).

Using FlashXMLHttpRequest is quite easy, as Couvreur has written a JavaScript wrapper that makes it work superficially like the XMLHttpRequest object. Figure 1 shows a simple example that executes an HTTP GET request of a Yahoo web service. (It searches for podcasts that have the word "fun" in the title or description and returns only the first result found.) The file FlashXMLHttpRequest.js, referenced at the top of the example, detects the version of Flash in the client browser and loads the appropriate SWF file.

<html>

<head>

<!-- Bring in the Dojo interface libraries and the FlashXMLHttpRequest wrapper -->  

<script type="text/javascript" src="../dojo-0.3.0-ajax/dojo.js"></script>

<script type="text/javascript" src="FlashXMLHttpRequest.js"></script>



<script type="text/javascript">

var xhr = null;

// Initialize the Flash interface and specify an "onload" function as the argument 

InitFlash("doMyCall");// This is the callback function for the FlashXMLHttpRequest below 

function showPodcast() {

       // Just display the raw XML response in an alert box 

       alert(xhr.responseText);

}

function doMyCall() {

       // Create the request object 

       xhr = new FlashXMLHttpRequest();

       // This is our Web Services call 

       var p = 'http://api.search.yahoo.com/AudioSearchService/V1/podcastSearch?appid=YahooDemo&query=fun&results=1';

       // Specify the callback function 

       xhr.onload = showPodcast;

       // Set the Content-Type header

       xhr.setRequestHeader("Content-Type", "text/xml");

       // Make the request 

       xhr.open('GET', p);

       xhr.send("");

  }

</script>

</head>

<body>

</body>

</html>

Code example: An HTTP GET using FlashXMLHttpRequest

FlashXMLHttpRequest suffers from a few limitations, some more annoying than others. It cannot receive HTTP status codes or send/receive HTTP headers, nor can it do HTTP PUT or DELETE, which limits its use by more sophisticated REST web services implementations. To be fair, these limitations are imposed by Flash 6 and 7; Flash 8.0 can receive HTTP status codes and send/receive HTTP headers, and Flash 8.5 will be able to handle HTTP PUTs and DELETEs.

Note that although the developer cannot explicitly send and receive HTTP headers, the HTTP Content-Type header can be set, which turns out to be quite useful if a type of data other than XML is requested, e.g., JSON or Serialized PHP. The request result is returned as a plain-text string, responseText. It is up to the developer to parse it into an object. The ability to send arbitrary HTTP headers will likely be a future enhancement to FlashXMLHttpRequest.

You can try out Julien Couvreur's demo, which demonstrates both HTTP GET and POST, here. Download the binary runtime here or the full source here.

Under Development

There are good security reasons why XMLHttpRequest is constrained by the Same Origin Policy mentioned earlier. But there are ways to design a pure JavaScript cross-domain request solution that will allow requests without compromising user security, and at least two proposals are on the table to add an additional function to future browsers that would allow cross-domain requests. Douglas Crockford's JSONRequest and Chris Holland's ContextAgnosticXMLHttpRequest are both proposals that allow HTTP requests from one domain to another while carefully considering the security ramifications.