Weird XMLHttpRequest error in IE – just one call allowed
Recently in a project I’ve been working on, I stumbled across something that I haven’t seen before: one AJAX call was possible to make in the web page, but after that it stopped working.
To give you some context: this (naturally) only occurred in Internet Explorer, both with the native XMLHttpRequest in IE 7 and an ActiveXObject for IE 6. A sidenote, but if anyone’s wondering: I use a try...catch statement to find the supported ActiveXObject in the visiting web browser:
try {
this.xmlHttp = new ActiveXObject("Msxml2.XMLHTTP.4.0");
}
catch(e){
try {
this.xmlHttp = new ActiveXObject("MSXML2.XMLHTTP");
}
catch(e){
try {
this.xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
}
catch(e){
this.xmlHttp = null;
}
}
}
My unsuccessful approach
My initial approach was to create the object once, and then use it for the consecutive calls when needed. This would be opposed to creating an object, making the calls and then deleting the object for each AJAX call. Silly little me thought this would be good for performance, and basically just refrain from superfluous object creation. But, apparently, the call just “stuck” in IE after it was completed, preventing the possibility to make any more calls.
A cut down version of the (originally object-oriented and fancy
) code was:
var xmlHttp = null;
/*
This function was called as soon as the page
had loaded, so xmlHttp was always available
*/
function createXMLHttpRequest(){
if(typeof XMLHttpRequest != "undefined"){
xmlHttp = new XMLHttpRequest();
}
else if(typeof window.ActiveXObject != "undefined"){
try {
xmlHttp = new ActiveXObject("Msxml2.XMLHTTP.4.0");
}
catch(e){
try {
xmlHttp = new ActiveXObject("MSXML2.XMLHTTP");
}
catch(e){
try {
xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
}
catch(e){
xmlHttp = null;
}
}
}
}
}
function getTheContent(url){
xmlHttp.abort(); // Tried both with and without this
xmlHttp.onreadystatechange = contentIsReady;
xmlHttp.open("GET", url, true);
xmlHttp.send(null);
}
function contentIsReady(){
if(xmlHttp && xmlHttp.readyState == 4){
// Do some magic
}
}
The one that worked
This is basically the same code as above, with the exception that the XMLHttpRequest object is created each time, and discarded once the call is done.
function createXMLHttpRequest(){
var xmlHttp = null;
if(typeof XMLHttpRequest != "undefined"){
xmlHttp = new XMLHttpRequest();
}
else if(typeof window.ActiveXObject != "undefined"){
try {
xmlHttp = new ActiveXObject("Msxml2.XMLHTTP.4.0");
}
catch(e){
try {
xmlHttp = new ActiveXObject("MSXML2.XMLHTTP");
}
catch(e){
try {
xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
}
catch(e){
xmlHttp = null;
}
}
}
}
return xmlHttp;
}
function getTheContent(url){
if(xmlHttp){
xmlHttp.abort();
}
// Create the object each time a call is about to be made
if(createXMLHttpRequest()){
xmlHttp.onreadystatechange = contentIsReady;
xmlHttp.open("GET", url, true);
xmlHttp.send(null);
}
}
function contentIsReady(){
if(xmlHttp && xmlHttp.readyState == 4){
// Do some magic
// Delete the object after the call is done
xmlHttp = null;
}
}
Any ides why it doesn’t work?
With those examples I have to ask you: do you have any ideas why it wouldn’t work with a single object creation? How come one call is ok, but it then just stops working?
26 Comments/Reactions
April 4th, 2007 at 9:12
I did a Google Maps mashup about 1,5 years ago, you know, when they we’re still hot. For that I used a few global variabels, one of which was a xmlHttp object. Since I didn’t know much about JS at the time I made the requests synchronous to avoid scope issues. It worked like a charm.
April 4th, 2007 at 10:46
Total guess… but I notice you’re setting
onreadystatechangebefore callingopen(which is different to the way things are normally done: e.g. prototype library, my own code, etc.)I don’t suppose setting the handler after calling
openfixes things does it?April 4th, 2007 at 11:22
Ash Searle’s right, IE resets the object when you call open() so you need to have your onreadystatechange after it. On a side note, you really should check your code before posting (I’m referring to getTheContent() ).
April 4th, 2007 at 13:15
Sjors,
I know that when making synchronous requests, there is (or at least was) a problem with Firefox and the
onreadystateevent.Ash,
Interesting. I know that the
onreadystatechangeevent needs to be applied before thesendevent, but then not necessarily before theopenmethod.Thanks! I’ll try it out!
Dave,
Thanks for confirming that.
Aww. I was trying to blog on a bouncy train. I have no idea where the extra indentation and erroneous
}came from…Anyway, I’ve updated the post with correct code.
April 4th, 2007 at 13:19
I know I’ve bumped into this problem with a project of mine but didn’t find any solution. Could that be it? So it means that you only open() once? Have to try it myself. Thanks for the tip, Ash.
April 4th, 2007 at 15:31
(totally nitpickerish off-topic; feel free to delete it)
“Huh?!†Is it really four-spaces indent I see in your examples?
April 4th, 2007 at 15:43
myf,
Ha ha!
Actually, it’s quite interesting. The pre-formatted source code is indeed indented with tabs, but when saving/publishing a post with WordPress, it replaces the tabs with spaces.
Man, that is a hawk eye thing to notice something like that!
April 5th, 2007 at 9:49
Hi – have you tried cache busting? I was building a “live price” system and found that IE would show the price changes once and then the requests wouldn’t continue.
I changed the URL and appended
Math.random()and it fixed the problem outright.April 5th, 2007 at 13:43
Remy,
Interesting approach. If the problem persists, it’s definitely something to try.
April 5th, 2007 at 16:48
Remy / Robert,
If you find IE’s using cached XHR responses, you’ve got a couple of things to looks at before resorting to brute-force timestamps:
1) Should you be using
"POST"instead of"GET"? (does the XHR request cause a permanent change on the server? If so, you should be using"POST"anyway.)2) Is the server setting appropriate
ExpiresandCache-Controlheaders on the XHR response?April 5th, 2007 at 20:04
Ash,
Very good points! Thank you for contributing!
April 7th, 2007 at 15:15
It’s definitely the fact that you assign the
onreadystatechangehandler before theopenmethod invocation. This was mentioned a while ago on the IE Team’s blog:(This means that you don’t need to invoke the
abortmethod, either.)April 7th, 2007 at 21:29
Nick,
Thanks for the info! However, the
abortmethod should still be important for other web browsers, right? To stop the current connection and start a new one.April 9th, 2007 at 18:19
Robert,
The W3C Draft Spec for the
open()method ofXMLHttpRequest states that:My rough and ready tests indicate that this isn’t necessarily how things work in the wild though… Firefox in particular seems to get a little upset if
abort()isn’t invoked, but gets upset a different way if it isApril 9th, 2007 at 21:42
Nick,
Hmm, interesting. When it comes to Firefox and the
abortmethod (a problem I’ve been facing as today, actually), the approach and description by Peter-Paul Koch described in XMLHTTP notes: abort() and Mozilla bug solves it.July 27th, 2007 at 14:03
Thanks! This blog entry saved me a good hour of debugging!
July 27th, 2007 at 14:05
John,
I’m glad it helped!
August 28th, 2007 at 12:24
i think the we dont need such complication if the a slight change is made:
getTheContent()
{
createXMLHttpRequest();
blah..
blah..
blah..
}
October 2nd, 2007 at 21:49
Seems like the IE cache issue @ first glance.
It will cache EVERYTHING in the GET scope, setting timeouts/headers etc doesn’t matter.
I just set the “ID” of may AJAX request (I’m using conio which still uses the initial JSON-RPC spec) to a random number (could append to ID if you’re actually using it for something)
October 2nd, 2007 at 22:37
LongStone,
At the end, it was the order of the code that was the culprit.
October 15th, 2007 at 20:21
Thanks Ash and Dale! Thanks Robert!
I had the same problem, IE7 allowed only one request and then XHR “died”. Solution really was to set XHR.onreadystatechange after calling XHR.open();
Cheers!
October 16th, 2007 at 11:56
Gurdi,
I’m glad it helped!
December 15th, 2007 at 9:13
I have tried everything that you guys have mentioned above … but am still getting “permission Denied” error beyond the first XHR Call
December 17th, 2007 at 10:13
Nik,
The reason might that you try to run files iles locally, which won’t work in Internet Explorer. Makes sure they’re on a server and things should work fine for you (I hope).
June 20th, 2008 at 21:35
I’m experiencing this issue as well with IE7. I tried both prototype and jQuery in an attempt to see if it was isolated to prototype.
Still haven’t figured this one out yet and it’s quite puzzling (especially since Microsoft’s AJAX libraries with ASP.NET AJAX work fine…).
The odd thing is that it all works fine if I’m running Fiddler (which kind of makes me wonder whether it’s a caching issue). As soon as I shut off Fiddler, I get 400/”Bad Request” errors.
In order to inspect the HTTP request, I had to use WireShark on the server (since Fiddler on the client was causing it to work for every request). It turns out that the requests are drastically different on successful attempts and failed attempts. Notably, even though the requests are identical, the content length is always 0 on failed requests.
Here is a failed request:
POST /FirstPoint.Common.Services.UserDriven/UserDrivenService/GetRoutes HTTP/1.1
Accept: text/javascript, text/html, application/xml, text/xml, */*
Accept-Language: en-us
x-prototype-version: 1.6.0.2
Referer: http://zorch6:2345/_layouts/FirstPointUserDrivenRoutes/Default.aspx
x-requested-with: XMLHttpRequest
Content-Type: application/json; charset=UTF-8
UA-CPU: x86
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.2; WOW64; .NET CLR 1.1.4322; .NET CLR 2.0.50727; .NET CLR 3.0.04506.30; InfoPath.2; MS-RTC LM 8; .NET CLR 3.0.04506.648; .NET CLR 3.5.21022)
Host: zorch6:2345
Content-Length: 0
Connection: Keep-Alive
Cache-Control: no-cache
Cookie: MSOWebPartPage_AnonymousAccessCookie=2345; WSS_KeepSessionAuthenticated=2345; previousLoggedInAs=; loginAsDifferentAttemptCount=
Authorization: NTLM TlRMTVNTUAABAAAAB7IIogkACQA0AAAADAAMACgAAAAFAs4OAAAAD0NIQVJMRVMtUVVBRFdPUktHUk9VUA==
Here is a successful request:
POST /FirstPoint.Common.Services.UserDriven/UserDrivenService/GetRoutes HTTP/1.1
Accept: text/javascript, text/html, application/xml, text/xml, */*
Accept-Language: en-us
x-prototype-version: 1.6.0.2
Referer: http://zorch6:2345/_layouts/FirstPointUserDrivenRoutes/Default.aspx
x-requested-with: XMLHttpRequest
Content-Type: application/json; charset=UTF-8
UA-CPU: x86
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.2; WOW64; .NET CLR 1.1.4322; .NET CLR 2.0.50727; .NET CLR 3.0.04506.30; InfoPath.2; MS-RTC LM 8; .NET CLR 3.0.04506.648; .NET CLR 3.5.21022)
Host: zorch6:2345
Content-Length: 16
Proxy-Connection: Keep-Alive
Pragma: no-cache
Cookie: MSOWebPartPage_AnonymousAccessCookie=2345; WSS_KeepSessionAuthenticated=2345; previousLoggedInAs=; loginAsDifferentAttemptCount=
And here is a request from FF (in which the AJAX request works flawlessly):
POST /FirstPoint.Common.Services.UserDriven/UserDrivenService/GetRoutes HTTP/1.1
Accept: text/javascript, text/html, application/xml, text/xml, */*
Accept-Language: en-us
x-prototype-version: 1.6.0.2
Referer: http://zorch6:2345/_layouts/FirstPointUserDrivenRoutes/Default.aspx
x-requested-with: XMLHttpRequest
Content-Type: application/json; charset=UTF-8
UA-CPU: x86
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.2; WOW64; .NET CLR 1.1.4322; .NET CLR 2.0.50727; .NET CLR 3.0.04506.30; InfoPath.2; MS-RTC LM 8; .NET CLR 3.0.04506.648; .NET CLR 3.5.21022)
Host: zorch6:2345
Content-Length: 16
Proxy-Connection: Keep-Alive
Pragma: no-cache
Cookie: MSOWebPartPage_AnonymousAccessCookie=2345; WSS_KeepSessionAuthenticated=2345; previousLoggedInAs=; loginAsDifferentAttemptCount=
Prototype has the correct ordering of calls to XHR to initiate the request, so I’m baffled by what could be causing this issue.
Any thoughts would be appreciated.
- Chuck
October 17th, 2008 at 21:15
Charles,
I’m having the same problem as you and think I have some idea about what’s happening but not the cause.
Your failed request includes a “Authorization: NTLM …” line and I’ll be that the payload of your request never made it to the server. If you aren’t sending anything with the send() method, then you’re probably not noticing it.
I believe that the browser is attempting to send a request to authenticate the request before it actually bothers to post the data your requesting.
At least that’s what I’m seeing in my code when I look at it with Fiddler.
Write a comment
Twitter reactions