Using Time Stamp Parameters to Avoid Stale Responses To Redirected Requests
I recently ran into an annoying bug caused by Internet Explorer caching the content of redirected requests. My fix involved adding a time stamp valued parameter on to the URL returned with the 302 (redirect) status code. The new parameter serves to ensure that the URL to which the client is being redirected is unique by virtue of the ‘cache busting’ time stamp. Since the URL will never have been seen before, the browser will not attempt to use a cached copy. The only caveat here is that if your server clock is ever reset, there is an astronomically small chance that a millisecond value previously sent to a given browser might be reused. In this very rare case the browser will not attempt a fetch in response to the redirect directive because it will mistakenly believe that it already has a copy of the content associated with the (previously seen) URL.
I was initially going to solve the problem by jamming in cache control headers, but I find these tricky to get right (although after reading this article I’m pretty sure that specifying max-age=0 is the thing to do, although no-store seems like it would serve as well.) In any case, for now, I’m sticking with my cache busting parameter approach. It seems very straight forward and virtually (modulo the caveats above) foolproof.
Reproducing the Incorrect Behavior
I’ve attached a code sample to illustrate how this technique can be used in a simple controller. To reproduce the “caching-of-redirected-requests” bug you must use Internet Explorer. Since FireFox works fine I’m guessing that one of these browsers is not performing to specification, but I’m not sure which one. In any case, my task was to support IE, so my fix ensures corrects behavior on this browser platform (as well as FireFox, and presumably others as well.) To get started, download the attached grails project zip archive, change to the ‘books’ directory, then run the application via the command:
grails run-app
Don’t change the port from 8080 because the example hard codes that. From IE, type the URL:
http://localhost:8080/books/book/getViaRedirect?doRedirect=true&addToResponse=haha
You will see the message ‘addition is haha’ written to standard out. This logs the fact that the content of the file /results.html is being appended to (with whatever value you specify via addToResponse).
Your browser will show the string:
content that will be sent as response to a redirected request haha
If you continue to type
http://localhost:8080/books/book/getViaRedirect?doRedirect=true&addToResponse=haha
you will see the same message being printed to standard out as the contents of results.html gains more and more lines of ‘haha’. But the response rendered in the browser will not change (because IE is using the cached value of http://localhost:8080/books/results.html.)
Now, if you repeat this same test wtih Firefox you will see the length of the response content grow with each request, like this:
content that will be sent as response to a redirected request haha
content that will be sent as response to a redirected request haha haha
content that will be sent as response to a redirected request haha haha haha
This is because you are seeing the most recently updated contents of the file that backs http://localhost:8080/books/results.html.
Code Walk-Through
The getViaRedirect action of BookController is fully reproduced below.
1 def getViaRedirect = {
2 if (params.doRedirect == "true") {
3 String fileSystemPath =
4 servletContext.getRealPath("/result.html")
5 println "fileSystemPath is $fileSystemPath"
6
7 File responseFile = new File(fileSystemPath)
8
9 String addition = ""
10 if (params.addToResponse) {
11 addition += "\n" + params.addToResponse
12 println "addition is $addition"
13 }
14
15 responseFile.text += addition
16
17 if (System.properties("cacheBust") == "true"){
18 redirect(url: "http://localhost:8080/books/result.html",
19 params: [cacheBuster: new Date().getTime()]
20 )
21 } else {
22 redirect(url: "http://localhost:8080/books/result.html")
23 }
24 }
25 else {
26 render("no redirecting. Here is some static text.")
27 }
28 }
On lines 3 and 4 we get the actual file system path of the /results.html resource and we (conditionally) append the string value of the ‘addToResponse’ parameter to the end of that file. On lines 17 through 22 we redirect the browser to requery for /results.html. The test at line 17 ensures that if you start grails with the -DcacheBust=true option, then the URL will be augmented with the cache busting parameter.
To try out the fix all you need to do is restart Grails via the command:
grails -DcacheBust=true run-app
Then try to reproduce the incorrect behavior with IE using the steps given above. This time you will see the correctly updated contents of the /results.html file.
Have fun !
























on January 5th, 2010
[...] is designed to solve the Stale Responses To Redirected Requests problem that I wrote about in a prior post. Basically, the problem has to do with a bug in IE that makes your Ajax requests seem to do nothing [...]