Friday, April 18th, 2008
Category: XmlHttpRequest
, IE
, Browsers
Kris Zyp gives us a glimpse at a potential future with his 100 line Ajax wrapper that tries to do the right thing cross browser for the various cross-domain models:
With IE8’s new XDomainRequest feature, a new API is added for cross-site requests, instead of using the W3C cross-site access proposal. Just for fun, I thought I would provide a little glimpse of what the classic Ajax request wrapper function may look like for the next era of web developers. Just a few simple calls to XMLHttpRequest would be way too easy, so instead we get do this:
JAVASCRIPT:
-
-
function doRequest(method,url,async,onLoad,onProgress) {
-
var xhr;
-
if ((onProgress || isXDomainUrl(url)) && window.XDomainRequest) {
-
// if it is x-domain or streaming/incremental updates are needed we will use IE's XDomainRequest for IE
-
// streaming/interactive mode is broken in IE's XHR, but for some reason works in XDR (with onprogress), so we will
-
// need to use XDR if incremental updates are necessary
-
// see bug https://connect.microsoft.com/IE/feedback/ViewFeedback.aspx?FeedbackID=334813
-
if (url.match(/^https:/) && !onProgress) {
-
// XDR doesn’t work for secure https communication
-
// see bug https://connect.microsoft.com/IE/feedback/ViewFeedback.aspx?FeedbackID=333380
-
loadUsingScriptTag(url); // script tag insertion can be more secure than XDR
-
// in some situations because it supports https
-
return;
-
}
-
xhr = new XDomainRequest;
-
// relative paths don’t work in XDomainRequest, see bug https://connect.microsoft.com/IE/feedback/ViewFeedback.aspx?FeedbackID=333275
-
if (!url.match(/^http:/)) { // test to see if it is an absolute url
-
url = absoluteUrl(location.href,url); // must have a function to turn it into an absolute url
-
}
-
if (!(method == “GET” || method == “POST”)) {
-
// XDomainRequest does not support methods besides GET and POST
-
// see bug https://connect.microsoft.com/IE/feedback/ViewFeedback.aspx?FeedbackID=334809
-
// We will try to add the method as a parameter and hope the server will understand… good luck :/
-
url += “&method=” + method;
-
method = “POST”;
-
}
-
function xdrLoad() {
-
if (xhr.contentType.match(/\/xml/)){
-
// there is no responseXML in XDomainRequest, so we have to create it manually
-
var dom = new ActiveXObject(”Microsoft.XMLDOM”);
-
dom.async = false;
-
dom.loadXML(xhr.responseText,200);
-
onLoad(dom);
-
}
-
else {
-
onLoad(xhr.responseText,200); // we will assume that the status code is 200, XDomainRequest rejects all other successful status codes
-
// see bug https://connect.microsoft.com/IE/feedback/ViewFeedback.aspx?FeedbackID=334804
-
}
-
}
-
if (async === false) {
-
// XDomainRequest does not support synchronous requests
-
// see bug https://connect.microsoft.com/IE/feedback/ViewFeedback.aspx?FeedbackID=336031
-
// so we will try to block execution on our own (which is not really possible in any reasonable manner)
-
var loaded;
-
xhr.onload = function() {
-
loaded = true;
-
xdrLoad();
-
}
-
xhr.open(method,url);
-
xhr.send(null);
-
while(!loaded) { // try to block until the response is received
-
// I am sure the user won’t mind just clicking OK so we can block execution
-
alert(”Waiting for the response, please click OK because it probably is here now”);
-
}
-
return;
-
}
-
else { // do an asynchronous request with XDomainRequest
-
xhr.onload = xdrLoad;
-
xhr.open(method,url);
-
xhr.onprogress = onProgress;
-
}
-
}
-
// we will mercifully skip all the branches for ActiveXObject(”Microsoft.XMLHTTP”) to accomodate IE6 and lower
-
else {
-
xhr = new XMLHttpRequest; // use the standard XHR for same origin and browsers that implement cross-site
-
// W3C requests and streaming
-
xhr.open(method,url,async);
-
xhr.onreadystatechange = function() {
-
if (xhr.readyState == 3) // interactive mode
-
onProgress(xhr.responseText);
-
if (xhr.readyState == 4) // finished
-
onLoad(xhr.responseText,xhr.status);
-
}
-
}
-
xhr.send(null); // finally send the request whether it be XDR or XHR
-
-
// and supporting functions
-
function absoluteUrl : function(baseUrl, relativeUrl) {
-
// This takes a base url and a relative url and resolves the target url.
-
// For example:
-
// resolveUrl(”http://www.domain.com/path1/path2″,”../path3″) ->”http://www.domain.com/path1/path3″
-
//
-
if (relativeUrl.match(/\w+:\/\//))
-
return relativeUrl;
-
if (relativeUrl.charAt(0)==’/') {
-
baseUrl = baseUrl.match(/.*\/\/[^\/]+/)
-
return (baseUrl ? baseUrl[0] : ”) + relativeUrl;
-
}
-
//TODO: handle protocol relative urls: ://www.domain.com
-
baseUrl = baseUrl.substring(0,baseUrl.length - baseUrl.match(/[^\/]*$/)[0].length);// clean off the trailing path
-
if (relativeUrl == ‘.’)
-
return baseUrl;
-
while (relativeUrl.substring(0,3) == ‘../’) {
-
baseUrl = baseUrl.substring(0,baseUrl.length - baseUrl.match(/[^\/]*\/$/)[0].length);
-
relativeUrl = relativeUrl.substring(3);
-
}
-
return baseUrl + relativeUrl;
-
}
-
function loadUsingScriptTag(url) {
-
… do JSONP here if we want
-
}
-
}
-
I think that says.... please just implement the standard IE team! :)
Wednesday, April 16th, 2008
Category: XmlHttpRequest
, Standards
The W3C has issued a last call on the XMLHttpRequest spec:
The Web API Working Group has published the Last Call Working Draft of The XMLHttpRequest Object. The XMLHttpRequest Object specification defines an API that provides scripted client functionality for transferring data between a client and a server. Comments are welcome through 2 June. Learn more about the Rich Web Client Activity.
It is nice to see things all speced out, including the fact that you can now get back real errors (SECURITY_ERR, NETWORK_ERR, ABORT_ERR).
There are future thoughts for the spec.next too:
load event and onload attribute;
error event and onerror attribute;
progress event and onprogress attribute;
abort event and onabort attribute;
- Timers have been suggested, perhaps an
ontimeout attribute;
- Property to disable following redirects;
responseXML for text/html documents;
- Cross-site
XMLHttpRequest;
responseBody to deal with byte streams;
overrideMimeType to fix up MIME types;
getRequestHeader() and
removeRequestHeader().
If you have some final thoughts, let them know!
Tuesday, April 15th, 2008
Category: XmlHttpRequest
, Dojo
Neil Roberts goes into the XHR Plugins that Dojo uses and how you can extend the system to have your own.
If you look at dojo.xhrGet you will see "Acceptable values are: text (default), json, json-comment-optional, json-comment-filtered, javascript, xml", but:
What you may not know is that the handleAs parameter is merely a way of specifying what plugin to use. Knowing where these plugins are, how they work, and how they can be adapted to suit your project will allow you to make repetitive tasks easy and less error-prone.
He starts by piggybacking on the json style callback, and adds a hook so when you do a query, if there are updated objects out there, they come back in the JSON so you can deal with them without having an extra remote call:
JAVASCRIPT:
-
-
dojo._contentHandlers.json = (function(old){
-
return function(xhr){
-
var json = old(xhr);
-
if(json.updated){
-
processUpdatedObjects(json.updated);
-
delete json.updated;
-
}
-
return json;
-
}
-
})(dojo._contentHandlers.json);
-
Next, Neil goes on to write a system that auto updates nodes:
JAVASCRIPT:
-
-
dojo.xhrGet({
-
url: "node-updates.php",
-
handleAs: "node-update-server"
-
});
-
-
dojo.xhrGet({
-
url: "node-content.php?node=sidebar",
-
node: "sidebar",
-
handleAs: "node-update"
-
});
-
which is as easy as:
JAVASCRIPT:
-
-
dojo.mixin(dojo._contentHandlers, {
-
"node-update-server": function(xhr){
-
var json = dojo._contentHandlers.json(xhr);
-
dojo.forEach(json.updates, function(update){
-
var node = dojo.byId(update.id);
-
if(node){
-
node.innerHTML = update.html;
-
}
-
});
-
},
-
"node-update": function(xhr){
-
var node = dojo.byId(xhr.args.node);
-
if(node){
-
node.innerHTML = dojo._contentHandlers.text(xhr);
-
}
-
}
-
});
-
In conclusion:
Dojo makes it incredibly easy to change the way that your Ajax calls work. You can change the format of JSON your server returns without having to change any of your callbacks, you can change the handleAs type for a single function to change the data given to your callback, you can get rid of callbacks altogether and use the arguments to your xhr call determine what should be done with your results.
Thursday, January 10th, 2008
Category: XmlHttpRequest
, Security
John Resig has written up documentation of Cross-Site XMLHttpRequest that discusses the W3C Access Control working draft which Firefox 3 implements.
He gives us a nice example:
In a nutshell, there are two techniques that you can use to achieve your desired cross-site-request result: Specifying a special Access-Control header for your content or including an access-control processing instruction in your XML.
In HTML:
PHP:
-
-
<?php header('Access-Control: allow <*>');
?>
-
<b>John Resig</b>
-
In XML:
XML:
-
-
<?xml version="1.0" encoding="UTF-8"?>
-
<?access-control allow="*"?>
-
<simple><name>John Resig</name></simple>
-
And the XHR code itself isn't different from any other XHR code:
JAVASCRIPT:
-
-
var xhr = new XMLHttpRequest();
-
xhr.open("GET", "http://dev.jquery.com/~john/xdomain/test.php", true);
-
xhr.onreadystatechange = function(){
-
if ( xhr.readyState == 4 ) {
-
if ( xhr.status == 200 ) {
-
document.body.innerHTML = "My Name is: " + xhr.responseText;
-
} else {
-
document.body.innerHTML = "ERROR";
-
}
-
}
-
};
-
xhr.send(null);
-
Some are excited to see the cross domain work, and some are concerned.... e.g.
I agree with Thomas. I never understood the NEED to modify the client security model to allow for this. If this is something the software needs to do, then the developer can implement a proxy on the server side. At least in this way the developer has sole discretion on the connections. Just more to go wrong if you ask me.
-
I'm still under the impression - and correct me if I'm wrong - that all these means are tailored to protect the server and its documents. But I thought the issue was to protect the client!
-
What exactly is the reason we need this? Has anybody here really understood why XMLHttp is currently limited to one host and cannot communicate cross-domain? I really do not understand that. If XMLHttp cannot do this by default, why it is still possible to load scripts and images from other servers? Why can I do exactly the same type of cross-domain communication using Flash, maybe using Silverlight in the future? What is the original reason for this limitation? Is this documented anywhere?
If, as mentioned in the spec, HTTP DELETE is problematic, because it may delete data, why cannot we filter such actions when detecting a cross-domain communication? GET and POST are possible in the same way when submitting simple form. It is even possible to generate these form elements dynamically. And this also works cross-domain. At least these two HTTP methods should be enabled by default to allow cross-domain communication. The open web, as often mentioned by Alex Russell, really needs features comparable with closed source software e.g. Flash or Silverlight.
-
I agree with those saying that this spec is misguided. But bothering users too much is also not good. How are they to know in every case what things mean?
What do you think?
Wednesday, November 21st, 2007
Category: XmlHttpRequest
, Security
The W3C has a new proposal titled Enabling Read Access for Web Resources which defines access control primitives to be used for cross domain XHR.
You can set control via a HTTP header:
HTML:
-
-
Access-Control: allow <*.example.org> exclude <*.public.example.org>
-
or an XML processing instruction:
XML:
-
-
<?access-control allow="allow.example.org" deny="deny.example.org"?>
-
so no cross_domain.xml.
Kris Zyp has written up his thoughts on the proposal:
A number of things to understand about this proposal:
- It does not create any new vulnerabilities with existing servers. Cross domain XHR will always fail with existing servers until they have specifically added headers to define the access control. In other words it doesn’t add new vulnerabilities to the web, rather it allows those who want to add cross site access the ability to due it in a secure manner without hacks like JSONP or fragment identifier messaging.
- Both GET and POST can currently be executed cross site with scripts tags or form submission, so many threats such as CSRF and DOS already exist, the proposal does not introduce them.
- The proposal states that cookies should be removed from cross site requests. This will reduce the incident of cross site request forgery, and forces developers to use more secure explicit forms of authentication maintanence.
- Developers that allow cross site access still must ensure that they are not providing privileged information to sites that should not be accessing the information. Developers that allow POST and other modifying operations should take similiar precautions.
- This provides a fine-grained access control level. When servers define access control headers that allow cross site access, they can specify which web page domains are allowed to access their resources.
Kris also has a comparison with JSON approaches:

The core XMLHttpRequest object itself has also seen a recent new draft.
Friday, September 15th, 2006
Category: XmlHttpRequest
, Toolkit
, Programming
According to Harry Fuecks in this post on the SitePoint PHP blog, using Ajax should be easier:
The Catch 22 of AJAX is, for the sake of an easy life, most of the time we want to write “synchronous code” but asynchronous is the only way to avoid some rather nasty usability issues. This means rather than being able to write simple code, as we’d like to. We’re required instead to handle this via callbacks, but that’s now introduced a whole load more potential issues.
These issues he mentions include requiring a global XMLHttpRequest object to be available and handling multiple calls to a javascript function (like if the user gets a little too impatient). To help combat these issues, Harry recommends a two projects out there that have the functionality to make life a little bit simpler:
Wednesday, August 9th, 2006
Category: Ajax
, XmlHttpRequest
, Security
Chris Shiflett has posted his look today at cross-domain Ajax requests and some of the security implications that can come with it, especially in a world where more and more developers are beginning to think it's okay.
Since the birth of Ajax (the term, not the technology), there has been an increasing interest in various client-side technologies, especially JavaScript. Those who have forged ahead in an attempt to innovate new ways of applying Ajax have inevitably run into the same-domain security policy of XMLHttpRequest(). As a result, there has been an increasing demand for cross-domain Ajax, and there are several creative techniques in use today to get around the same-domain restriction (none of which I consider cross-domain Ajax).
He talks about other methods that can capture the data in an Ajax request (post scanner), but notes that one of the real dangers is removing a barrier for cross-site request forgeries that most normal sites already have in place.
To illustrate, he mentions an issue Digg had with a "self-digging story" a little while back. He also includes a sort of how-to on the method that they used to accomplish the task - basically a Javascript form submit on each viewing. There are checks in place for