Cross-Site XHR Plugin Registry

July 31st, 2008 - by Kris Zyp

Several new technologies are coming out for accessing resources from other sites. IE8 will include the XDomainRequest and the W3C is finishing the access control specification for enabling cross-site access for XMLHttpRequest, which will probably be implemented in next major release of all browsers, including Firefox. However, it will certainly take a long time for these new technologies to become pervasive enough to use them reliably and exclusively. Is there any way to leverage this technology immediately when these browsers are released? Yes! With Dojo’s new XHR plugin system, you can start making cross-site requests right away, using the new technologies when available, and a proxy server as a fallback, all while using the familiar dojo.xhr* methods. Your source code can still use this simple API while the registry can handle choosing the appropriate underlying transports for the situation.

Using the XHR Plugin Registry

Suppose we need to securely access a web service from othersite.com. This server can easily support the W3C Access Control specification by including this header:

Access-Control: allow <*>

Now we can use cross-site XHR and XDomainRequest handling for XHR calls to this destination by simply calling:

dojox.io.xhrPlugins.addCrossSiteXhr("http://othersite.com/");

Now on IE8, if you call:

dojo.xhrGet({url:"http://othersite.com/data"})

This request can be handled directly in the browser with XDomainRequest. Similarly in other browsers that implement cross-site capability in the XmlHttpRequest API, this will result in an XHR call.

You can also use the window.name module as an XHR plugin as well. There is a specific module to make this easy to add, so you can simply call:

dojo.require("dojox.io.xhrWindowNamePlugin");
dojox.io.xhrWindowNamePlugin("http://othersite.com/");

Proxy

These plugins will utilize the browser’s cross-site request capability when available, however, you still need a fallback for older browsers. One of the most common techniques for doing this is with a proxy service. To create a proxy service, you simply need to create a resource (servlet, php file, etc.) that can take a parameter and then make a request from the target site and return the results. Once you have created a proxy service, we can add it as an XHR plugin:

dojox.io.xhrPlugins.addProxy("/proxy?url=");

This handles the remaining use cases. Any request to a foreign URL that can’t be handled by the browser will be sent to the local /proxy URL with the url parameter set to the foreign URL. Now if we call:

var deferred = dojo.xhrGet({url:"http://othersite.com/data"});
or
var deferred = dojo.xhr("GET",{url:"http://othersite.com/data"});

This will be handled directly from the browser (which is faster and more efficient) when possible, and will go to through proxy when the browser can’t handle it. Local XHR calls will still be handled by the normal XMLHttpRequest object. If we call:

var deferred = dojo.xhrGet({url:"/myLocalData"})

There will be no difference in behavior, the registry automatically chooses the original XHR handler for this request. Other handlers could be added to the registry as well. For certain applications and servers, it may be possible to use a JSONP (which is unfortunately not secure) handler or iframe proxy as a substitute cross-site XHR handler.

Security Notes

When using a proxy, there may be certain headers from the original request that can be passed on, but generally you should not pass on cookies, as they may have sensitive information from your site. You should also use proper filtering mechanisms, as blindly routing HTML or scripts can lead to cross-site scripting security holes.

HTTP Adapters

Some applications may rely on certain HTTP level meta-data like headers, methods, and status codes. However, not all transports necessarily have access to the HTTP layer in order to define or access this information. Rather than requiring application logic to be modified to transfer meta-data in alternate way, you can define a convention for converting HTTP meta-data to something that is accessible to the underlying transport at the XHR API level, in order to retain this information. Both addCrossSiteXhr and xhrWindowNamePlugin have an optional second argument that can take a function. This function will be called with the default XHR handler as an argument, and should return a function that will be called for each XHR call. You can build a custom HTTP adapter, or the xhrPlugins module provides a default converter that can easily be used:

dojox.io.xhrWindowNamePlugin("http://othersite.com/",dojox.io.xhrPlugins.fullHttpAdapter);

This adapter will automatically convert the HTTP method to a parameter named http-method in the query string of the request URL. It will also add any defined headers to the query string with an http- prefix. Consequently calling:

dojo.xhr("PUT",{url:"http://othersite.com/page",headers:{"Range":"0-20 bytes"}});

Would result in a request URL:

http://othersite.com/page?http-method=PUT&http-Range=0-20%20bytes

You can even build your own HTTP adapter for custom conversions.

Conclusion

The XHR plugin registry allows you to explicitly define and use different transports that are used in different situations based on browser and server support, while still allowing the call-site syntax to remain a simple call to a dojo.xhr function. All this is done through explicit techniques to avoid magic and surprises. This allows for a clean separation of request triggering and transport definition, allowing transports to evolve for cross-domain loading while not affecting the application logic that relies on requests and can remain agnostic to the underlying transport mechanism. The XHR plugin registry will be available when Dojo 1.2 is released, and is available in the current Dojo nightly builds.

Bookmark and Share

Tags:

7 Responses to “Cross-Site XHR Plugin Registry”

  1. [...] leading the charge on various missions such as JSON-* and XHR-*. This time he has a posting on a new cross-site XHR plugin repository that wraps up the myriad of techniques that are both pending in standards (XDomain, XHR++) and work [...]

  2. [...] Via Ajaxian, Kris Zyp introduces the Dojo Cross-Site XHR Plugin registry [...]

  3. Kyle Simpson says:

    I would love to see flXHR (http://flxhr.flensed.com/) get plugin support as well. There’s already a demo on the site which shows how to adapt Dojo to use flXHR (which is easy since flXHR already uses the native XHR API), but this plugin system looks like it would be even better!

  4. Kris Zyp says:

    Kyle, that sounds like a good idea to me, that would be a nice addition to the registry for servers that only allow access through crossdomain.xml.

  5. Kyle Simpson says:

    @Kris-
    How would I go about learning how to make a plugin for Dojo that would properly substitute in flXHR so that its suitable for this repository?

    As Demo#7 (http://flxhr.flensed.com/demo.php#demo7) on the flXHR site shows, right now my easy suggested way to ‘adapt’ Dojo to use flXHR is to simply overwrite the dojo._xhrObj() XHR factory function and force it to return an flXHR instance instead. Since flXHR speaks the same API as normal native XHR, this isn’t a problem.

    But it sounds like a plugin would be a lot more direct and robust (and supportable), so I’d love to do that. I just don’t know how to get started.

  6. [...] what cross-domain loading techniques are available for this target site by registering with the XHR plugin system. For example, if barbie.com supports the window.name protocol for secure data transfer, we [...]

  7. Kyle Simpson says:

    I’ve finally gotten around to releasing the Dojo plugin for flXHR: http://flxhr.flensed.com/dojo.php If this plugin itself (not flXHR in general) is still something you’d be interested in having donated to Dojo foundation, I’m certainly willing, please just let me know.

Leave a Reply