Fixes #2994. Not finding a transport now fires the error callbacks and doesn't make ajax return false. Had to revise how jsonp and script prefilters & transports work (better separation of concerns). Also took the opportunity to revise jXHR getRequestHeader and abort methods and enabled early transport garbage collection when the request completes.
This commit is contained in:
parent
d9cb69873c
commit
8ab23aec2c
52
src/ajax.js
52
src/ajax.js
|
@ -306,9 +306,7 @@ jQuery.extend({
|
|||
// (match is used internally)
|
||||
getResponseHeader: function( key , match ) {
|
||||
|
||||
if ( state !== 2 ) {
|
||||
return null;
|
||||
}
|
||||
if ( state === 2 ) {
|
||||
|
||||
if ( responseHeaders === undefined ) {
|
||||
|
||||
|
@ -321,15 +319,22 @@ jQuery.extend({
|
|||
}
|
||||
}
|
||||
}
|
||||
return responseHeaders[ key.toLowerCase() ];
|
||||
match = responseHeaders[ key.toLowerCase() ];
|
||||
|
||||
} else {
|
||||
|
||||
match = null;
|
||||
}
|
||||
|
||||
return match;
|
||||
},
|
||||
|
||||
// Cancel the request
|
||||
abort: function( statusText ) {
|
||||
if ( transport && state !== 2 ) {
|
||||
if ( transport ) {
|
||||
transport.abort( statusText || "abort" );
|
||||
done( 0 , statusText );
|
||||
}
|
||||
done( 0 , statusText );
|
||||
return this;
|
||||
}
|
||||
};
|
||||
|
@ -347,6 +352,10 @@ jQuery.extend({
|
|||
// State is "done" now
|
||||
state = 2;
|
||||
|
||||
// Dereference transport for early garbage collection
|
||||
// (no matter how long the jXHR transport will be used
|
||||
transport = 0;
|
||||
|
||||
// Set readyState
|
||||
jXHR.readyState = status ? 4 : 0;
|
||||
|
||||
|
@ -599,22 +608,14 @@ jQuery.extend({
|
|||
s.data = jQuery.param( s.data , s.traditional );
|
||||
}
|
||||
|
||||
// Get transport
|
||||
transport = jQuery.ajaxPrefilter( s , options ).ajaxTransport( s );
|
||||
// Apply prefilters
|
||||
jQuery.ajaxPrefilter( s , options );
|
||||
|
||||
// Watch for a new set of requests
|
||||
if ( s.global && jQuery.active++ === 0 ) {
|
||||
jQuery.event.trigger( "ajaxStart" );
|
||||
}
|
||||
|
||||
// If no transport, we auto-abort
|
||||
if ( ! transport ) {
|
||||
|
||||
done( 0 , "transport not found" );
|
||||
jXHR = false;
|
||||
|
||||
} else {
|
||||
|
||||
// More options handling for requests with no content
|
||||
if ( ! s.hasContent ) {
|
||||
|
||||
|
@ -665,19 +666,30 @@ jQuery.extend({
|
|||
|
||||
// Abort if not done already
|
||||
done( 0 , "abort" );
|
||||
|
||||
// Return false
|
||||
jXHR = false;
|
||||
|
||||
} else {
|
||||
|
||||
// Set state as sending
|
||||
state = 1;
|
||||
jXHR.readyState = 1;
|
||||
|
||||
// Install callbacks on deferreds
|
||||
for ( i in { success:1, error:1, complete:1 } ) {
|
||||
jXHR[ i ]( s[ i ] );
|
||||
}
|
||||
|
||||
// Get transport
|
||||
transport = jQuery.ajaxTransport( s );
|
||||
|
||||
// If no transport, we auto-abort
|
||||
if ( ! transport ) {
|
||||
|
||||
done( 0 , "notransport" );
|
||||
|
||||
} else {
|
||||
|
||||
// Set state as sending
|
||||
state = jXHR.readyState = 1;
|
||||
|
||||
// Send global event
|
||||
if ( s.global ) {
|
||||
globalEventContext.trigger( "ajaxSend" , [ jXHR , s ] );
|
||||
|
|
|
@ -11,9 +11,7 @@ jQuery.ajaxSetup({
|
|||
return "jsonp" + jsc++;
|
||||
}
|
||||
|
||||
// Normalize jsonp queries
|
||||
// 1) put callback parameter in url or data
|
||||
// 2) sneakily ensure transportDataType is always jsonp for jsonp requests
|
||||
// Detect, normalize options and install callbacks for jsonp requests
|
||||
}).ajaxPrefilter("json jsonp", function(s, originalSettings) {
|
||||
|
||||
if ( s.dataTypes[ 0 ] === "jsonp" ||
|
||||
|
@ -22,8 +20,10 @@ jQuery.ajaxSetup({
|
|||
jsre.test(s.url) ||
|
||||
typeof(s.data) === "string" && jsre.test(s.data) ) {
|
||||
|
||||
var jsonpCallback = s.jsonpCallback =
|
||||
var responseContainer,
|
||||
jsonpCallback = s.jsonpCallback =
|
||||
jQuery.isFunction( s.jsonpCallback ) ? s.jsonpCallback() : s.jsonpCallback,
|
||||
previous = window[ jsonpCallback ],
|
||||
url = s.url.replace(jsre, "$1" + jsonpCallback + "$2"),
|
||||
data = s.url === url && typeof(s.data) === "string" ? s.data.replace(jsre, "$1" + jsonpCallback + "$2") : s.data;
|
||||
|
||||
|
@ -33,16 +33,6 @@ jQuery.ajaxSetup({
|
|||
|
||||
s.url = url;
|
||||
s.data = data;
|
||||
s.dataTypes[ 0 ] = "jsonp";
|
||||
}
|
||||
|
||||
// Bind transport to jsonp dataType
|
||||
}).ajaxTransport("jsonp", function(s) {
|
||||
|
||||
// Put callback in place
|
||||
var responseContainer,
|
||||
jsonpCallback = s.jsonpCallback,
|
||||
previous = window[ jsonpCallback ];
|
||||
|
||||
window [ jsonpCallback ] = function( response ) {
|
||||
responseContainer = [response];
|
||||
|
@ -65,9 +55,6 @@ jQuery.ajaxSetup({
|
|||
|
||||
}, s.complete ];
|
||||
|
||||
// Sneakily ensure this will be handled as json
|
||||
s.dataTypes[ 0 ] = "json";
|
||||
|
||||
// Use data converter to retrieve json after script execution
|
||||
s.converters["script json"] = function() {
|
||||
if ( ! responseContainer ) {
|
||||
|
@ -76,8 +63,12 @@ jQuery.ajaxSetup({
|
|||
return responseContainer[ 0 ];
|
||||
};
|
||||
|
||||
// Delegate to script transport
|
||||
// force json dataType
|
||||
s.dataTypes[ 0 ] = "json";
|
||||
|
||||
// Delegate to script
|
||||
return "script";
|
||||
}
|
||||
});
|
||||
|
||||
})( jQuery );
|
||||
|
|
|
@ -15,18 +15,22 @@ jQuery.ajaxSetup({
|
|||
"text script": jQuery.globalEval
|
||||
}
|
||||
|
||||
// Bind script tag hack transport
|
||||
}).ajaxTransport("script", function(s) {
|
||||
// Handle cache's special case and global
|
||||
}).ajaxPrefilter("script", function(s) {
|
||||
|
||||
// Handle cache special case
|
||||
if ( s.cache === undefined ) {
|
||||
s.cache = false;
|
||||
}
|
||||
|
||||
// This transport only deals with cross domain get requests
|
||||
if ( s.crossDomain && s.async && ( s.type === "GET" || ! s.data ) ) {
|
||||
|
||||
if ( s.crossDomain ) {
|
||||
s.global = false;
|
||||
}
|
||||
|
||||
// Bind script tag hack transport
|
||||
}).ajaxTransport("script", function(s) {
|
||||
|
||||
// This transport only deals with cross domain requests
|
||||
if ( s.crossDomain ) {
|
||||
|
||||
var script,
|
||||
head = document.getElementsByTagName("head")[0] || document.documentElement;
|
||||
|
|
|
@ -1865,25 +1865,19 @@ test("jQuery ajax - failing cross-domain", function() {
|
|||
|
||||
var i = 2;
|
||||
|
||||
if ( jQuery.ajax({
|
||||
jQuery.ajax({
|
||||
url: 'http://somewebsitethatdoesnotexist-67864863574657654.com',
|
||||
success: function(){ ok( false , "success" ); },
|
||||
error: function(xhr,_,e){ ok( true , "file not found: " + xhr.status + " => " + e ); },
|
||||
complete: function() { if ( ! --i ) start(); }
|
||||
}) === false ) {
|
||||
ok( true , "no transport" );
|
||||
if ( ! --i ) start();
|
||||
}
|
||||
});
|
||||
|
||||
if ( jQuery.ajax({
|
||||
jQuery.ajax({
|
||||
url: 'http://www.google.com',
|
||||
success: function(){ ok( false , "success" ); },
|
||||
error: function(xhr,_,e){ ok( true , "access denied: " + xhr.status + " => " + e ); },
|
||||
complete: function() { if ( ! --i ) start(); }
|
||||
}) === false ) {
|
||||
ok( true , "no transport" );
|
||||
if ( ! --i ) start();
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
@ -1937,7 +1931,7 @@ test( "jQuery.ajax - statusCode" , function() {
|
|||
404: function() {
|
||||
ok( ! isSuccess , name );
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
jQuery.each( {
|
||||
|
|
Loading…
Reference in a new issue