Search code examples
javascriptajaxxmlhttprequest

Intercept XMLHttpRequest and modify responseText


I'm trying to build a script that will act as a proxy/wrapper for the native XMLHttpRequest object enabling me to intercept it, modify the responseText and return back to the original onreadystatechange event.

The context being, if the data the app is trying to receive is already available in local storage, to abort the XMLHttpRequest and pass the locally stored data back into the apps success/failure callback methods. Assume I have no control over the apps existing AJAX callback methods.

I had originally tried the following idea..

var send = XMLHttpRequest.prototype.send;
XMLHttpRequest.prototype.send = function(data){
   //Do some stuff in here to modify the responseText
   send.call(this, data);
};

But as I have now established, the responseText is read only.

I then tried taking a step back, writing my own full native proxy to XMLHttpRequest, ultimately ending up writing my own version of the native methods. Similar to what is discussed here...

http://www.ilinsky.com/articles/XMLHttpRequest/#implementation-wrapping

But it rapidly got confusing, and still have the difficulty of returning the modified data back into the original onReadyStateChange method.

Any suggestions? Is this even possible?


Solution

  • //
    // firefox, ie8+ 
    //
    var accessor = Object.getOwnPropertyDescriptor(XMLHttpRequest.prototype, 'responseText');
    
    Object.defineProperty(XMLHttpRequest.prototype, 'responseText', {
    	get: function() {
    		console.log('get responseText');
    		return accessor.get.call(this);
    	},
    	set: function(str) {
    		console.log('set responseText: %s', str);
    		//return accessor.set.call(this, str);
    	},
    	configurable: true
    });
    
    
    //
    // chrome, safari (accessor == null)
    //
    var rawOpen = XMLHttpRequest.prototype.open;
    
    XMLHttpRequest.prototype.open = function() {
    	if (!this._hooked) {
    		this._hooked = true;
    		setupHook(this);
    	}
    	rawOpen.apply(this, arguments);
    }
    
    function setupHook(xhr) {
    	function getter() {
    		console.log('get responseText');
    
    		delete xhr.responseText;
    		var ret = xhr.responseText;
    		setup();
    		return ret;
    	}
    
    	function setter(str) {
    		console.log('set responseText: %s', str);
    	}
    
    	function setup() {
    		Object.defineProperty(xhr, 'responseText', {
    			get: getter,
    			set: setter,
    			configurable: true
    		});
    	}
    	setup();
    }