Flash to the Rescue
June 28, 2006
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
GET
s and POST
s on your Ajax application's behalf.
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 PUT
s and DELETE
s.
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.