Using an ASP.NET UpdatePanel behind a reverse proxy

After putting my ASP.NET web forms application that utilized UpdatePanels (and therefore the ASP.NET AJAX WebForms library) behind a reverse proxy, we found that all of our AJAX enabled content was coming up with the error:

Sys.WebForms.PageRequestManagerParserErrorException: The message received from the server could not be parsed. Common causes for this error are when the response is modified by calls to Response.Write(), response filters, HttpModules, or server trace is enabled.

The error detail is actually quite instructive, you’re not meant to mess with the responses from AJAX calls after they’ve been written out by ASP.NET…

But what about if something later in the “pipeline” comes along and manipulates your response, for example, my reverse proxy that is rewriting all of my http://my-internal-server:1080/myapplication/ to http://www.my-domain.com/some-path/my-application …?

That one seems like a totally legit reason to update a content stream to me…

So… Here’s a fix that you could try (be warned – it may not be production ready!), try adding the following javascript that will hack it’s way into the AJAX WebForms client side scripts to manipulate the response stream before it gets parsed:


// Check if the AJAX forms page request manager script objects are initialized...
// If not, then you'll need to add this later in the document body!
if (window.Sys && window.Sys.WebForms && window.Sys.WebForms.PageRequestManager) {
	var oldParseDelta = null;
	var newParseDelta = function(executor) {
		// Replace the executor object's get_responseData function with our extended version.
		var oldGetResponseData = executor.get_responseData;
		if (oldGetResponseData) {
			executor.get_responseData = function() {
				// Get the original data
				var data = oldGetResponseData.call(this);

				// Fix it up
				// Ajax data is formatted in pipe separated chunks of 4 with the first value indicating the string length of the last.
				// Seeing as though we've possibly messed with the last value (through URL rewriting), we have to update the first value.
				// They come in sets like: 4|abc|def|four|
				// So that "4" indicates how long the string "four" is.
				var fixedData = data.slice(0);
				var chunker = /(\d+)\|([^\|]*)\|([^\|]*)\|([^\|]*)\|/gi;
				var match = chunker.exec(data);
				while (match != null) {
					fixedData = fixedData.replace(match[0], match[4].length + "|" + match[2] + "|" + match[3] + "|" + match[4] + "|")
				    match = chunker.exec(data);
				}

				// Return our fixed data
				return fixedData;
			}
		}
		return oldParseDelta.call(this, executor);
	}

	// We need to extend the "getInstance" function to allow us to get hooks into the _parseDelta function
	var oldGetInstance = window.Sys.WebForms.PageRequestManager.getInstance;
	if (oldGetInstance) {
		window.Sys.WebForms.PageRequestManager.getInstance = function() {
			var instance = oldGetInstance.call(this);
			// Be careful to only replace once!
			if (instance._parseDelta !== newParseDelta) {
				oldParseDelta = instance._parseDelta;
				instance._parseDelta = newParseDelta;
			}
			return instance;
		};
	}
}

Hopefully the above code is a little self explanatory… But basically, it hacks itself into the response parsing pipeline (on the client-side) and re-parses and updates the length indicators that the server side of AJAX WebForms inserts (because they’ll be made invalid by the reverse proxy URL rewriter).

Hope it helps!

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s