aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMichał Gołębiowski-Owczarek <m.goleb@gmail.com>2023-02-01 13:40:55 +0100
committerGitHub <noreply@github.com>2023-02-01 13:40:55 +0100
commit6d1364431b63b0d3bbe1c5fd604131f9db453396 (patch)
tree9410a7891baaf2bf3296eca08aba9d3f2fc51d9c
parentb02a257f98688aa890e06a85672cd1a54c3ffa3a (diff)
downloadjquery-6d1364431b63b0d3bbe1c5fd604131f9db453396.tar.gz
jquery-6d1364431b63b0d3bbe1c5fd604131f9db453396.zip
Ajax: Support `headers` for script transport even when cross-domain
The AJAX script transport has two versions: XHR + `jQuery.globalEval` or appending a script tag (note that `jQuery.globalEval` also appends a script tag now, but inline). The former cannot support the `headers` option which has so far not been taken into account. For jQuery 3.x, the main consequence was the option not being respected for cross-domain requests. Since in 4.x we use the latter way more often, the option was being ignored in more cases. The transport now checks whether the `headers` option is specified and uses the XHR way unless `scriptAttrs` are specified as well. Fixes gh-5142 Closes gh-5193
-rw-r--r--src/ajax/script.js28
-rw-r--r--test/unit/ajax.js104
2 files changed, 98 insertions, 34 deletions
diff --git a/src/ajax/script.js b/src/ajax/script.js
index fee8a66e0..aa8ddb4c5 100644
--- a/src/ajax/script.js
+++ b/src/ajax/script.js
@@ -6,20 +6,28 @@ import "../ajax.js";
function canUseScriptTag( s ) {
// A script tag can only be used for async, cross domain or forced-by-attrs requests.
+ // Requests with headers cannot use a script tag. However, when both `scriptAttrs` &
+ // `headers` options are specified, both are impossible to satisfy together; we
+ // prefer `scriptAttrs` then.
// Sync requests remain handled differently to preserve strict script ordering.
- return s.crossDomain || s.scriptAttrs ||
+ return s.scriptAttrs || (
+ !s.headers &&
+ (
+ s.crossDomain ||
- // When dealing with JSONP (`s.dataTypes` include "json" then)
- // don't use a script tag so that error responses still may have
- // `responseJSON` set. Continue using a script tag for JSONP requests that:
- // * are cross-domain as AJAX requests won't work without a CORS setup
- // * have `scriptAttrs` set as that's a script-only functionality
- // Note that this means JSONP requests violate strict CSP script-src settings.
- // A proper solution is to migrate from using JSONP to a CORS setup.
- ( s.async && jQuery.inArray( "json", s.dataTypes ) < 0 );
+ // When dealing with JSONP (`s.dataTypes` include "json" then)
+ // don't use a script tag so that error responses still may have
+ // `responseJSON` set. Continue using a script tag for JSONP requests that:
+ // * are cross-domain as AJAX requests won't work without a CORS setup
+ // * have `scriptAttrs` set as that's a script-only functionality
+ // Note that this means JSONP requests violate strict CSP script-src settings.
+ // A proper solution is to migrate from using JSONP to a CORS setup.
+ ( s.async && jQuery.inArray( "json", s.dataTypes ) < 0 )
+ )
+ );
}
-// Install script dataType. Don't specify `content.script` so that an explicit
+// Install script dataType. Don't specify `contents.script` so that an explicit
// `dataType: "script"` is required (see gh-2432, gh-4822)
jQuery.ajaxSetup( {
accepts: {
diff --git a/test/unit/ajax.js b/test/unit/ajax.js
index 166d31de0..fec1d9565 100644
--- a/test/unit/ajax.js
+++ b/test/unit/ajax.js
@@ -71,35 +71,91 @@ QUnit.module( "ajax", {
};
} );
- ajaxTest( "jQuery.ajax() - custom attributes for script tag", 5,
- function( assert ) {
- return {
- create: function( options ) {
- var xhr;
- options.method = "POST";
- options.dataType = "script";
- options.scriptAttrs = { id: "jquery-ajax-test", async: "async" };
- xhr = jQuery.ajax( url( "mock.php?action=script" ), options );
- assert.equal( jQuery( "#jquery-ajax-test" ).attr( "async" ), "async", "attr value" );
- return xhr;
- },
- beforeSend: function( _jqXhr, settings ) {
- assert.strictEqual( settings.type, "GET", "Type changed to GET" );
- },
- success: function() {
- assert.ok( true, "success" );
- },
- complete: function() {
- assert.ok( true, "complete" );
- }
- };
- }
- );
+ jQuery.each( [ " - Same Domain", " - Cross Domain" ], function( crossDomain, label ) {
+ ajaxTest( "jQuery.ajax() - custom attributes for script tag" + label, 5,
+ function( assert ) {
+ return {
+ create: function( options ) {
+ var xhr;
+ options.crossDomain = crossDomain;
+ options.method = "POST";
+ options.dataType = "script";
+ options.scriptAttrs = { id: "jquery-ajax-test", async: "async" };
+ xhr = jQuery.ajax( url( "mock.php?action=script" ), options );
+ assert.equal( jQuery( "#jquery-ajax-test" ).attr( "async" ), "async", "attr value" );
+ return xhr;
+ },
+ beforeSend: function( _jqXhr, settings ) {
+ assert.strictEqual( settings.type, "GET", "Type changed to GET" );
+ },
+ success: function() {
+ assert.ok( true, "success" );
+ },
+ complete: function() {
+ assert.ok( true, "complete" );
+ }
+ };
+ }
+ );
+
+ ajaxTest( "jQuery.ajax() - headers for script transport" + label, 3,
+ function( assert ) {
+ return {
+ create: function( options ) {
+ Globals.register( "corsCallback" );
+ window.corsCallback = function( response ) {
+ assert.strictEqual( response.headers[ "x-custom-test-header" ],
+ "test value", "Custom header sent" );
+ };
+ options.crossDomain = crossDomain;
+ options.dataType = "script";
+ options.headers = { "x-custom-test-header": "test value" };
+ return jQuery.ajax( url( "mock.php?action=script&callback=corsCallback" ), options );
+ },
+ success: function() {
+ assert.ok( true, "success" );
+ },
+ complete: function() {
+ assert.ok( true, "complete" );
+ }
+ };
+ }
+ );
+
+ ajaxTest( "jQuery.ajax() - scriptAttrs winning over headers" + label, 4,
+ function( assert ) {
+ return {
+ create: function( options ) {
+ var xhr;
+ Globals.register( "corsCallback" );
+ window.corsCallback = function( response ) {
+ assert.ok( !response.headers[ "x-custom-test-header" ],
+ "headers losing with scriptAttrs" );
+ };
+ options.crossDomain = crossDomain;
+ options.dataType = "script";
+ options.scriptAttrs = { id: "jquery-ajax-test", async: "async" };
+ options.headers = { "x-custom-test-header": "test value" };
+ xhr = jQuery.ajax( url( "mock.php?action=script&callback=corsCallback" ), options );
+ assert.equal( jQuery( "#jquery-ajax-test" ).attr( "async" ), "async", "attr value" );
+ return xhr;
+ },
+ success: function() {
+ assert.ok( true, "success" );
+ },
+ complete: function() {
+ assert.ok( true, "complete" );
+ }
+ };
+ }
+ );
+ } );
ajaxTest( "jQuery.ajax() - execute JS when dataType option is provided", 3,
function( assert ) {
return {
create: function( options ) {
+ Globals.register( "corsCallback" );
options.crossDomain = true;
options.dataType = "script";
return jQuery.ajax( url( "mock.php?action=script&header=ecma" ), options );