#8177 closed bug (patchwelcome)
Browser inconsistencies with ajax, ifmodified, notmodified and ApplicationCache
Reported by: | blabber | Owned by: | blabber |
---|---|---|---|
Priority: | low | Milestone: | 1.next |
Component: | ajax | Version: | 1.5 |
Keywords: | Cc: | jaubourg | |
Blocked by: | Blocking: |
Description
jQuery.ajax does not return consistent results for requests on files from the ApplicationCache, online files and when setting ifModified to true or false. I have done a number of test on different browsers / operating systems. A test consists of 4 different requests, each is executed two times in a row:
- Read file from application cache, ifModified = false.
- Read file from network, ifModified = false.
- Read file from application cache, ifModified = true.
- Read file from network, ifModified = true.
The results below show 6 different results. I discovered the problem because of the problems with Android 2.2 and Safari 5.0 returning no data and 'notmodified' status for normal request. This problem only occures, if the browser is closed with the application cache filled with the test case!
The fix this, ifModified can be checked before setting the textStatus to 'notmodified'. Otherwise jQuery can just return the responseText otherwise.
The patch would be to change:
if ( status === 304 ) {
to
if ( s.ifModified && status === 304 ) {
I have not further investigated the problems with ifModified set to true and I am not even sure, what the correct result should be. I would guess the first result block is ok. Firefox 4.0b, Safari, Chrome and newer androud versions seem to have some bugs?
Firefox 3.6.13 Ubuntu 10.10, Firefox 3.6.12 Windows XP, IE 8.0.7600.16385 Windows 7, IE 9.0.7930.16406 Windows 7, Android 1.6 Browser cached1.txt ifModified: false success data: ok jqXHR.responseText: ok textStatus: success cached1.txt ifModified: false success data: ok jqXHR.responseText: ok textStatus: success online1.txt ifModified: false success data: ok jqXHR.responseText: ok textStatus: success online1.txt ifModified: false success data: ok jqXHR.responseText: ok textStatus: success cached2.txt ifModified: true success data: ok jqXHR.responseText: ok textStatus: success cached2.txt ifModified: true success data: - jqXHR.responseText: - textStatus: notmodified online2.txt ifModified: true success data: ok jqXHR.responseText: ok textStatus: success online2.txt ifModified: true success data: - jqXHR.responseText: - textStatus: notmodified Firefox 4.0b10 Windows 7 cached1.txt ifModified: false success data: ok jqXHR.responseText: ok textStatus: success cached1.txt ifModified: false success data: ok jqXHR.responseText: ok textStatus: success online1.txt ifModified: false success data: ok jqXHR.responseText: ok textStatus: success online1.txt ifModified: false success data: ok jqXHR.responseText: ok textStatus: success cached2.txt ifModified: true success data: ok jqXHR.responseText: ok textStatus: success cached2.txt ifModified: true success data: ok jqXHR.responseText: ok textStatus: success online2.txt ifModified: true success data: ok jqXHR.responseText: ok textStatus: success online2.txt ifModified: true success data: ok jqXHR.responseText: ok textStatus: success Google Chrome 8.0.552.237 Windows XP, Google Chrome 9.0.597.84 beta Ubuntu 10.10, Android Browser 2.2 (? Samsung Tab) cached1.txt ifModified: false success data: ok jqXHR.responseText: ok textStatus: success cached1.txt ifModified: false success data: ok jqXHR.responseText: ok textStatus: success online1.txt ifModified: false success data: ok jqXHR.responseText: ok textStatus: success online1.txt ifModified: false success data: ok jqXHR.responseText: ok textStatus: success cached2.txt ifModified: true success data: ok jqXHR.responseText: ok textStatus: success cached2.txt ifModified: true success data: ok jqXHR.responseText: ok textStatus: success online2.txt ifModified: true success data: ok jqXHR.responseText: ok textStatus: success online2.txt ifModified: true success data: - jqXHR.responseText: - textStatus: notmodified Android Browser 2.2 (? Samsung Tab) ApplicationCache loaded before browser start cached1.txt ifModified: false success data: - jqXHR.responseText: ok textStatus: notmodified cached1.txt ifModified: false success data: - jqXHR.responseText: ok textStatus: notmodified online1.txt ifModified: false success data: ok jqXHR.responseText: ok textStatus: success online1.txt ifModified: false success data: ok jqXHR.responseText: ok textStatus: success cached2.txt ifModified: true success data: - jqXHR.responseText: ok textStatus: notmodified cached2.txt ifModified: true success data: - jqXHR.responseText: ok textStatus: notmodified online2.txt ifModified: true success data: ok jqXHR.responseText: ok textStatus: success online2.txt ifModified: true success data: ok jqXHR.responseText: ok textStatus: success Safari 5.0 (7533.16) Windows XP, Safari 5.03 (6533.19.4) MacOSX ApplicationCache loaded in same browser session cached1.txt ifModified: false success data: ok jqXHR.responseText: ok textStatus: success cached1.txt ifModified: false success data: ok jqXHR.responseText: ok textStatus: success online1.txt ifModified: false success data: ok jqXHR.responseText: ok textStatus: success online1.txt ifModified: false success data: ok jqXHR.responseText: ok textStatus: success cached2.txt ifModified: true success data: ok jqXHR.responseText: ok textStatus: success cached2.txt ifModified: true success data: ok jqXHR.responseText: ok textStatus: success online2.txt ifModified: true success data: ok jqXHR.responseText: ok textStatus: success online2.txt ifModified: true success data: - jqXHR.responseText: - textStatus: notmodified ApplicationCache loaded before browser start cached1.txt ifModified: false success data: - jqXHR.responseText: ok textStatus: notmodified cached1.txt ifModified: false success data: - jqXHR.responseText: ok textStatus: notmodified online1.txt ifModified: false success data: ok jqXHR.responseText: ok textStatus: success online1.txt ifModified: false success data: ok jqXHR.responseText: ok textStatus: success cached2.txt ifModified: true success data: - jqXHR.responseText: ok textStatus: notmodified cached2.txt ifModified: true success data: - jqXHR.responseText: ok textStatus: notmodified online2.txt ifModified: true success data: ok jqXHR.responseText: ok textStatus: success online2.txt ifModified: true success data: - jqXHR.responseText: - textStatus: notmodified
Change History (16)
comment:1 follow-up: 2 Changed 12 years ago by
Cc: | jaubourg added |
---|---|
Component: | unfiled → ajax |
Owner: | set to blabber |
Priority: | undecided → low |
Status: | new → pending |
comment:2 Changed 12 years ago by
Status: | pending → new |
---|
Replying to jitter:
I may have time to setup a running test case on a public server on monday. But I think I can also just post the code inline, as it is not too complicated or long.
Just create all files in a folder and access index.html. For Android and Safari it may change the results, if you close the browser and open it again with the application cache filled.
index.html:
<html manifest="cache.manifest"> <head> <script src="jquery.js"></script> </head> <body> <pre id="log"></pre> <script> function log(text) { $('#log').append(text + '<br>'); }; var i = 0; var urls = ['cached1.txt', 'online1.txt', 'cached2.txt', 'online2.txt']; function test() { if (i > 7) return; load(urls[Math.floor(i / 2)], i < 4 ? false : true); ++i; }; function load(url, ifModified) { $.ajax({ dataType: 'text', ifModified: ifModified, url: url }).success(function(data, textStatus, jqXHR) { log(url + ' ifModified: ' + (ifModified ? 'true ' : 'false') + ' success ' + ' data: ' + (data === url + 'x' ? 'ok ' : (!data ? '- ' : 'error')) + ' jqXHR.responseText: ' + (jqXHR.responseText === url + 'x' ? 'ok ' : (!jqXHR.responseText ? '- ' : 'error')) + ' textStatus: ' + textStatus); if (jqXHR.responseText && jqXHR.responseText !== url + 'x') log('responseText: ' + jqXHR.responseText); }).error(function(jqXHR, textStatus, errorThrown) { log(url + ' ifModified: ' + (ifModified ? 'true ' : 'false') + ' error ' + ' data: - ' + ' jqXHR.responseText: ' + (jqXHR.responseText === url + 'x' ? 'ok ' : ((!jqXHR.responseText) ? '- ' : 'error')) + ' textStatus: ' + textStatus); if (jqXHR.responseText && jqXHR.responseText !== url + 'x') log('responseText: ' + jqXHR.responseText); }).complete(function () { test(); }); }; $(document).ready(function() { window.setTimeout(test, 2000); }); </script> </body> </html>
cache.manifest:
CACHE MANIFEST #v1 NETWORK: online1.txt online2.txt CACHE: index.html jquery.js cached1.txt cached2.txt
cached1.txt, cached2.txt, online1.txt, online2.txt Content is filename + 'x', e.g.:
cached1.txtx
and obviously you will need jquery.js in the folder.
comment:3 Changed 12 years ago by
Resolution: | → fixed |
---|---|
Status: | new → closed |
Fixes #8177. XHR transport now considers 304 Not Modified responses as 200 OK if no conditional request header was provided (as per the XMLHttpRequest specification).
Changeset: d6fbbe1080fdcaf8eb22753eddf000aeb7d99545
comment:4 follow-up: 5 Changed 12 years ago by
Milestone: | 1.next → 1.5.1 |
---|
blabber, can you confirm this fixes your issue?
comment:5 follow-up: 8 Changed 12 years ago by
Edit: Tested the wrong version, with the fix I mentioned above. Sorry. The current git-Version does not fix the bug, but seems to give empty data and textStatus:
Safari (page loaded, Safari closed, Safari opened, page loaded again): cached1.txt ifModified: false error data: - jqXHR.responseText: ok textStatus: cached1.txt ifModified: false error data: - jqXHR.responseText: ok textStatus: online1.txt ifModified: false success data: ok jqXHR.responseText: ok textStatus: success online1.txt ifModified: false success data: ok jqXHR.responseText: ok textStatus: success cached2.txt ifModified: true error data: - jqXHR.responseText: ok textStatus: cached2.txt ifModified: true error data: - jqXHR.responseText: ok textStatus: online2.txt ifModified: true success data: ok jqXHR.responseText: ok textStatus: success online2.txt ifModified: true success data: - jqXHR.responseText: - textStatus: notmodified
(Edit: removed old content)
Replying to jaubourg:
blabber, can you confirm this fixes your issue?
comment:6 Changed 12 years ago by
Resolution: | fixed |
---|---|
Status: | closed → reopened |
comment:7 Changed 12 years ago by
Owner: | changed from blabber to jaubourg |
---|---|
Status: | reopened → assigned |
comment:8 follow-up: 9 Changed 12 years ago by
Replying to blabber:
Edit: Tested the wrong version, with the fix I mentioned above. Sorry. The current git-Version does not fix the bug, but seems to give empty data and textStatus:
Safari (page loaded, Safari closed, Safari opened, page loaded again): cached1.txt ifModified: false error data: - jqXHR.responseText: ok textStatus: cached1.txt ifModified: false error data: - jqXHR.responseText: ok textStatus: online1.txt ifModified: false success data: ok jqXHR.responseText: ok textStatus: success online1.txt ifModified: false success data: ok jqXHR.responseText: ok textStatus: success cached2.txt ifModified: true error data: - jqXHR.responseText: ok textStatus: cached2.txt ifModified: true error data: - jqXHR.responseText: ok textStatus: online2.txt ifModified: true success data: ok jqXHR.responseText: ok textStatus: success online2.txt ifModified: true success data: - jqXHR.responseText: - textStatus: notmodifiedThat was fast. :-)
The patch fixes the browser differences for the cases with ifModified=false. The differences found with ifModified=true remain as above.Replying to jaubourg:
blabber, can you confirm this fixes your issue?
I'm confused as to what you say here. What is fixed? What isn't fixed?
Would also help a bunch if your log lines were a bit more compact and featured the status code and error message. For instance:
cached1.text(true/false): statusCode, status (thirdParameterOfErrorCallbackIfError), data, jqXHR.responseText
This would definitely help me tackle this while not having to duplicate your effort.
comment:9 Changed 12 years ago by
This bug probably collects a number of differences in the way that browsers react to ajax request on files from the ApplicationCache and files from the network. The list in the original bug report gives the complete picture across a number of browser.
At least the different browser reactions to request with ifModified == true should be fixable, because the browsers provide all necessary information. I have provided a simple patch in the original bug report. I have no idea, if it is correct, but it works for my current project (giving success for all cases where ifModified == false).
There are more different cases, when ifModified == false, but I do not even know for sure, how jQuery should react in these cases.
Here are my current test results, Run 1 is identical for the current trunk and jQuery 1.5.
Run 1: Safari startet __without__ test page in ApplicationCache: File ifMod callback status data respTxt textStatus errorThrown 1 cached1.txt false success 200 ok ok success 2 cached1.txt false success 200 ok ok success 3 online1.txt false success 200 ok ok success 4 online1.txt false success 200 ok ok success 5 cached2.txt true success 200 ok ok success 6 cached2.txt true success 200 ok ok success 7 online2.txt true success 200 ok ok success 8 online2.txt true success 304 - - notmodified Run 2: Safari startet __with__ test page in ApplicationCache: jQuery Trunk 534dbd660eaefbbc5827b1b61ba384e768562086 File ifMod callback status data respTxt textStatus errorThrown 1 cached1.txt false error 0 - ok 2 cached1.txt false error 0 - ok 3 online1.txt false success 200 ok ok success 4 online1.txt false success 200 ok ok success 5 cached2.txt true error 0 - ok 6 cached2.txt true error 0 - ok 7 online2.txt true success 200 ok ok success 8 online2.txt true success 304 - - notmodified jQuery 1.5 File ifMod callback status data respTxt textStatus errorThrown 1 cached1.txt false success 304 - ok notmodified 2 cached1.txt false success 304 - ok notmodified 3 online1.txt false success 200 ok ok success 4 online1.txt false success 200 ok ok success 5 cached2.txt true success 304 - ok notmodified 6 cached2.txt true success 304 - ok notmodified 7 online2.txt true success 200 ok ok success 8 online2.txt true success 304 - - notmodified
index.html, other files as described above:
<html manifest="cache.manifest"> <head> <script src="jquery.js"></script> </head> <body> <pre id="log"> File ifMod callback status data respTxt textStatus errorThrown </pre> <script> function log(text) { $('#log').append(text + '<br>'); }; var i = 0; var urls = ['cached1.txt', 'online1.txt', 'cached2.txt', 'online2.txt']; function test() { if (i > 7) return; load(urls[Math.floor(i / 2)], i < 4 ? false : true); ++i; }; function load(url, ifModified) { $.ajax({ dataType: 'text', ifModified: ifModified, url: url }).success(function(data, textStatus, jqXHR) { log(i + ' ' + url + ' ' + (ifModified ? 'true ' : 'false ') + ' success ' + jqXHR.status + ' ' + (data === url + 'x' ? 'ok ' : (!data ? '- ' : 'err')) + ' ' + (jqXHR.responseText === url + 'x' ? 'ok ' : (!jqXHR.responseText ? '- ' : 'err')) + ' ' + textStatus); if (jqXHR.responseText && jqXHR.responseText !== url + 'x') log('responseText: ' + jqXHR.responseText); }).error(function(jqXHR, textStatus, errorThrown) { log(i + ' ' + url + ' ' + (ifModified ? 'true ' : 'false ') + ' error ' + jqXHR.status + ' - ' + ' ' + (jqXHR.responseText === url + 'x' ? 'ok ' : (!jqXHR.responseText ? '- ' : 'err')) + ' ' + textStatus + ' ' + errorThrown); if (jqXHR.responseText && jqXHR.responseText !== url + 'x') log('responseText: ' + jqXHR.responseText); }).complete(function () { test(); }); }; $(document).ready(function() { window.setTimeout(test, 1000); }); </script> </body> </html>
comment:10 follow-up: 11 Changed 12 years ago by
blabber, status for local files should always be 200 now. Be aware that empty files will trigger an error (that's due to some serious limitations in native xhr implementations). Waiting for your confirmation/infirmation.
comment:11 Changed 12 years ago by
Replying to jaubourg:
blabber, status for local files should always be 200 now. Be aware that empty files will trigger an error (that's due to some serious limitations in native xhr implementations). Waiting for your confirmation/infirmation.
Unfortunately that did not work. I have written a new patch for the current trunk, that tries to handle some of the cases in xhr.js. The patch does not change as much, as it seems to change, but mostly restores old tests and combines them in a slightly different way.
With the patch most browsers seem to work with ifModified = false. Case 6 still differs between browsers or even between access to localhost or access to ip address (for Firefox 3.6.13). Opera also acts chaotic (success with ip, error with localhost, error with ip from virtual machine...). Perhaps all this are just ApplicationCache problems. After all my tests I do not really trust any browser in this regard... ;-)
I will use my patched version of the trunk for now and give further feedback, if I run into any obvious problems, but do not have the time at the moment to do more extensive cross-browser ... tests. :-/
diff --git a/src/ajax/xhr.js b/src/ajax/xhr.js index 9c8790a..0bad811 100644 --- a/src/ajax/xhr.js +++ b/src/ajax/xhr.js @@ -166,14 +166,30 @@ if ( jQuery.support.ajax ) { } // Filter status for non standard behaviors - status = - // If the request is local and we have data: assume a success - // (success with no data won't get notified, that's the best we - // can do given current implementations) - !status && s.isLocal && !s.crossDomain ? - ( responses.text ? 200 : 404 ) : - // IE - #1450: sometimes returns 1223 when it should be 204 - ( status === 1223 ? 204 : status ); + + // IE - #1450: sometimes returns 1223 when it should be 204 + if ( status === 1223 ) { + status = 204; + // If the request is local and we have data: assume a success + // (success with no data won't get notified, that's the best we + // can do given current implementations) + } else if ( !status ) { + // Webkit, Firefox: filter out faulty cross-domain requests + if ( !s.crossDomain || statusText ) { + // Opera: filter out real aborts #6060 + if (responseHeaders || xml) { + status = + !headers[ "if-modified-since" ] && !headers[ "if-none-match" ] ? + 200 + : 304; + } else { + status = 0; + } + // We assume 302 but could be anything cross-domain related + } else { + status = 302; + } + } } } } catch( firefoxAccessException ) {
comment:12 follow-up: 14 Changed 12 years ago by
Milestone: | 1.5.1 → 1.next |
---|---|
Owner: | changed from jaubourg to blabber |
blabber, with current Trunk, you'll always get 200 OK for local files as long as they are not empty. This is because browser's xhr implementations are completely uninformative when it comes to the file system (no headers, no meaningful status code -- always 0 --, etc).
As of today, there is no mean to differentiate between all the different cases encompassed by a zeroed status code without breaking something, elsewhere, for another browser (and in the case of Opera, for the same browser).
I'll close this as patchwelcome but I don't expect one until browsers fix their many xhr inconsistencies.
comment:13 Changed 12 years ago by
Resolution: | → patchwelcome |
---|---|
Status: | assigned → closed |
comment:14 Changed 12 years ago by
Replying to jaubourg:
blabber, with current Trunk, you'll always get 200 OK for local files as long as they are not empty.
I am not talking about local files, but about the ApplicationCache. That may be local, but is probably not handled the same way as local files. Have you changed the trunk again, to really return 200 Ok for all files from the application cache? If not, my test results from above are probably still valid. The last time I checked the trunk, the results where worse than in 1.5, because the request returned an error instead of success.
I'll close this as patchwelcome but I don't expect one until browsers fix their many xhr inconsistencies.
I have provided 2 patches, one for the trunk and one for 1.5, that let the browsers return 200 Ok for files from the application cache. Did you use the information from these patches, to fix the problems? Did you setup the test case? If not: How can I help? What is wrong with my fixes? Did I miss any important points?
I am really trying to help...
comment:15 Changed 12 years ago by
@blabber,
I'm all for fixing bugs, believe me, but your patch worked only because of a very serious bug in 1.5 that made any failing ajax request from the local filesystem (and apparently Application Cache) issue a 304.
The situation with the native xhr status set to 0 with no mean to determine if it is a success or an error makes it impossible to lay out any kind of consistant logic that wouldn't break something elsewhere.
If the Application Cache issues a 0 status-code in a non local filesystem environment then we just can't assume it is a 200 OK: this would basically break every other environment.
Like I said, it's patch-welcome but I'm sorry to say it's probably up to the browsers' developpers at this point... and from what I've seen the last few months, I don't believe adding consistency to their xhr implementations to be a priority... which incidently happens to turn my life into a nightmare.
comment:16 Changed 12 years ago by
I am also seeing this problem. When I make a request for a resource that is in the ApplicationCache, I receive "success" the first time with the data being passed to the "success" callback. It even works if I hit refresh a bunch of times, but when I close the browser (android) and start it again; I get "notmodified" as the status text and "undefined" as the data.
Thanks for taking the time to contribute to the jQuery project by writing a bug report.
It would be nice if you could provide more information so that we are able to reproduce your tests by:
and by including/posting detailed instructions on what steps need to be taken (opening which page, refreshing, steps in which order, emptying cache, ....) to reproduce your results and at the same time describing what are the expected results for your different tests.
That would allow us to investigate this issue further, as it's difficult to tell by your description and the "output of your tests" what exactly is going on. Also make sure to read the link given below, in order to provide a most useful bug report.
How to report bugs