Bug Tracker

Opened 7 years ago

Closed 7 years ago

Last modified 7 years ago

#13654 closed bug (notabug)

Ajax response with status code 204 triggers fail() on cross-domain requests

Reported by: ojak Owned by: ojak
Priority: undecided Milestone: None
Component: unfiled Version: 1.9.1
Keywords: Cc: jaubourg
Blocked by: Blocking:

Description

Ajax response with status code 204 triggers fail() on cross-domain requests. Example

http://localhost:8080/served_page.html
  >> .ajax() to localhost:8080 with response 204 triggers .done()
  >> .ajax() to localhost:8081 with response 204 triggers .fail() with xhr.status = 0

Change History (15)

comment:1 Changed 7 years ago by ojak

A fix was attempted in ticket:13261 that seems to have only solved same domain requests.

comment:2 Changed 7 years ago by ojak

One note, the ajax request works when 'script' is specified as the dataType, but fails on 'text'. Since 204 is a success with No Content, a 204 response should always trigger a success unless there is any content, which would be an invalid 204.

I think the most correct way to handle this would probably be to define an expected dataType return of 'none' or 'empty'. Any thoughts?

Last edited 7 years ago by ojak (previous) (diff)

comment:3 Changed 7 years ago by dmethvin

Cc: jaubourg added

Julian can you take a look?

comment:4 Changed 7 years ago by jaubourg

I'm ready to look into this, with a proper test case ;)

Off the top of my head, if it's cross-domain, using script tag injection, then we don't get the status code anyway, so we just cannot handle it appropriately.

I'm really confused about the dataType juggling described but I cannot draw any conclusion out of vague descriptions: test cases are mandatory especially for ajax bugs.

Also, which browser ? What is the error triggered (the args given to the error callbacks) ? etc etc

comment:5 Changed 7 years ago by dmethvin

Owner: set to ojak
Status: newpending

We're awaiting a test case from the reporter then; @ojak please provide more information as requested above.

comment:6 Changed 7 years ago by ojak

Status: pendingnew

Here's some example code with 4 tests, the resulting firebug console logs, and sample nginx.conf to setup 204's on localhost:

https://gist.github.com/ojak/5241273

I tested on Firefox 19 and Chrome 25.0.1364.172 (although Chrome needs to be run with flags to avoid Access-Control-Allow-Origin issues).

Last edited 7 years ago by ojak (previous) (diff)

comment:7 Changed 7 years ago by jaubourg

All right:

  • for dataType "script", a script tag is used as a transport, so jQuery cannot access the actual status code, so it assumes 200 for success, 404 for error. There is no way around it. It is working as expected. If you wanna avoid the script transport, force crossDomain to false.
  • for dataType "text", could you log the actual arguments in the fail callback ? Logging "Fail" doesn't give much information as to why the request actually fails.

Thanks for the follow up btw. If you can log the arguments given to the fail handler in TEST4, I may be able to assess what's going on without having to setup the env you've already setup yourself.

comment:8 Changed 7 years ago by jaubourg

Status: newpending

comment:9 Changed 7 years ago by ojak

Status: pendingnew

I updated the gist on the fail with some xhr response information:

https://gist.github.com/ojak/5241273

--- TEST4: Test different port with 'text' dataType
GET http://localhost:8082/204 204 No Content
Fail
- xhr.status : 0
- xhr.statusText : error
- xhr.response : undefined
- xhr.responseText :
- xhr.responseXML : undefined
- textStatus : error
- errorThrown :
=== FAIL AND INCORRECT STATUSCODE

comment:10 Changed 7 years ago by jaubourg

Status: newpending

Hmmm, looks a lot like a network error (status code 0, no errorThrown). At least we know it's not the conversion logic going wrong.

Just out of curiosity, and before I dive deeper in there, does the request work using pure xhr?

Something along the lines of the following snippet (untested):

var xhr = $.ajaxSettings.xhr();

xhr.open( "GET", "http://localhost:8082/204" );

xhr.onsuccess = function() {
   console.log( "success", xhr, arguments );
};

xhr.onerror = function() {
   console.log( "error", xhr, arguments );
};

xhr.onabort = function() {
   console.log( "abort", xhr, arguments );
};

xhr.ontimeout = function() {
   console.log( "abort", xhr, arguments );
};

xhr.send();

Also, you should test with http://code.jquery.com/jquery-git.js so that you use jQuery latest (not that it should matter much but you never know).

comment:11 Changed 7 years ago by ojak

Status: pendingnew

Interesting. Tried that out and here are the results:

Screenshot of firebug results: http://s3.amazonaws.com/ojak/screenshot-jquery-xhr.png

Code to reproduce: https://gist.github.com/ojak/5247073

Same results against the current jQuery dist built from the master branch.

Last edited 7 years ago by ojak (previous) (diff)

comment:12 Changed 7 years ago by jaubourg

My guess is you have a Content-Type in your response headers that trigger the xml parsing logic in FF. Same in Chrome?

Sounds less and less like a jQuery bug...

comment:13 Changed 7 years ago by ojak

The attached screenshot shows the Response Header, which show that the response headers do not have a Content-Type:

http://s3.amazonaws.com/ojak/screenshot-xhr-response-headers.png

I still think it's a jQuery bug since 204s should not return a "Content-Type" from the server, since by definition there should be no content:

http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.5

But in the end, both Chrome and Firefox are seeing failures, so maybe that's the problem? There should probably be a dataType of "none" defined in jQuery, since the response is not failing.

Last edited 7 years ago by ojak (previous) (diff)

comment:14 Changed 7 years ago by jaubourg

Resolution: notabug
Status: newclosed

I think you should take this discussion into the forums or stackoverflow.

From what you showed, the response is not successful. The error handler is triggered on the native XHR. The native XHR is not part of jQuery just replace $.ajaxSettings.xhr() by new XMLHttpRequest() to convince yourself no jQuery code is used here.

What this means is that browsers seem to have a problem with your 204 responses. From what I gather from the error message in firebug, it seems as if Firefox is trying to parse the response as XML (not jQuery but Firefox itself), hence why I made the wild guess you had a Content-Type response header (a header that is provided by the server, not something jQuery would incante out of thin air). It doesn't seem to be the problem though. Maybe your 204 response has a body (I see an XML tab in http://s3.amazonaws.com/ojak/screenshot-xhr-response-headers.png which doesn't seem right at all), again something that is provided by the server not client-side code.

I dunno if it's a configuration problem server-side, a bug in the browsers, a problem specific to CORS or some convoluted behaviour that makes no sense but is actually standard. What's clear though is that jQuery is not responsible for the problem and no action on our part is needed nor possible: if the native XHR notifies an error with the wrong status code, jQuery just cannot "guess" what the original status code was.

I'll close this as notabug but, please, feel free to add a link to any forum or stackoverflow discussion/question related to the issue.

comment:15 Changed 7 years ago by ojak

Gotcha, thanks.

Just for a little extra color on the issue, there appears to be a 3+ year old bug in Firefox for this behavior:

https://bugzilla.mozilla.org/show_bug.cgi?id=521301

Anyhow, thanks so much for helping track down these issues. I think the best advice going forward for others coming across this issue is: "don't use 204's for AJAX requests, use 200's with empty content"... which is a bummer.

Might be worth updating the jQuery docs to mention this behavior as a warning?

Last edited 7 years ago by ojak (previous) (diff)
Note: See TracTickets for help on using tickets.