Skip to content

Instantly share code, notes, and snippets.

@furyutei
Last active April 8, 2022 18:09
Show Gist options
  • Save furyutei/adfda593f31b2dbf59f994768bef6c6a to your computer and use it in GitHub Desktop.
Save furyutei/adfda593f31b2dbf59f994768bef6c6a to your computer and use it in GitHub Desktop.
Userscriptで"Access-Control-Allow-Origin"ヘッダが返らないリソースへのアクセスを試行

ユーザースクリプトマネージャの違いによるCORS動作の差異を検証

"Access-Control-Allow-Origin"ヘッダが返らないリソースへのアクセス結果が、ユーザースクリプトマネージャによって異なることを検証するためのユーザースクリプト。

テスト用ユーザースクリプト

  1. CORS_TEST.user.js

テスト方法

上記ユーザースクリプトをインストールして、テスト用ぺージを開く。
※結果は、開発者ツールのコンソールに出力

// ==UserScript==
// @name CORS_TEST
// @namespace https://furyu.hatenablog.com/
// @version 0.1
// @description CORS TEST (Please access "https://nazo.furyutei.work/test/CORS/" and check the console log)
// @author furyu
// @match https://nazo.furyutei.work/test/CORS/*
// @grant GM_xmlhttpRequest
// @grant GM.xmlHttpRequest
// @connect furyu-tei.sakura.ne.jp
// @require https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js
// @require https://furyutei.github.io/jsTwitterOAuth/src/js/twitter-oauth/jQuery.setAjaxTransport_GM_xmlhttpRequest.js
// ==/UserScript==
( function () {
'use strict';
var TEST_URL_BASE = 'https://furyu-tei.sakura.ne.jp/',
get_test_url = function () {
return TEST_URL_BASE + '?t=' + new Date().getTime();
},
test_number = 0,
show_result = function ( test_number, args ) {
var result = ( typeof args[ 1 ] == 'string' ) ? args[ 1 ] : args[ 0 ].arguments[ 1 ];
if ( result == 'error' ) {
console.error( test_number, 'FAILURE:', args );
}
else {
console.log( test_number, 'SUCCESS:', args );
}
};
$.Deferred().resolve()
.then( function () {
console.log( ++test_number, 'TEST: XMLHttpRequest()' );
return $no_reject_wrapper( $promise_xmlhttprequest( get_test_url() ) );
} )
.then( function () {
show_result( test_number, arguments ); // Tampermonkey: FAILURE / Greasemonkey: SUCCESS
console.log( ++test_number, 'TEST: fetch()' );
return $no_reject_wrapper( $promise_fetch( get_test_url() ) );
} )
.then( function () {
show_result( test_number, arguments ); // Tampermonkey: FAILURE / Greasemonkey: SUCCESS
if ( ( typeof GM_xmlhttpRequest != 'function' ) && ( typeof GM.xmlHttpRequest == 'function' ) ) {
window.GM_xmlhttpRequest = GM.xmlHttpRequest;
}
console.log( ++test_number, 'TEST: GM_xmlhttpRequest()' );
return $no_reject_wrapper( $promise_gm_xmlhttprequest( get_test_url() ) );
} )
.then( function () {
show_result( test_number, arguments ); // Tampermonkey: SUCCESS / Greasemonkey: SUCCESS
console.log( ++test_number, 'TEST: $.ajax()' );
return $no_reject_wrapper( $.ajax( get_test_url() ) );
} )
.then( function () {
show_result( test_number, arguments ); // Tampermonkey: FAILURE / Greasemonkey: SUCCESS
if ( typeof $.setAjaxTransport_GM_xmlhttpRequest == 'function' ) {
$.setAjaxTransport_GM_xmlhttpRequest();
}
console.log( ++test_number, 'TEST: $.ajax() after $.setAjaxTransport_GM_xmlhttpRequest()' );
return $no_reject_wrapper( $.ajax( get_test_url() ) );
} )
.then( function () {
show_result( test_number, arguments ); // Tampermonkey: SUCCESS / Greasemonkey: SUCCESS
} );
function $promise_xmlhttprequest( url ) {
var $deferred = $.Deferred(),
$promise = $deferred.promise(),
xhr = new XMLHttpRequest(),
loadHandler = function () {
$deferred.resolve( xhr.responseText, 'success', xhr );
},
errorHandler = function () {
$deferred.reject( xhr, 'error' );
};
xhr.open( 'GET', url );
xhr.addEventListener( 'load', loadHandler, false );
xhr.addEventListener( 'error', errorHandler, false );
xhr.send( null );
return $promise;
}
function $promise_fetch( url ) {
var $deferred = $.Deferred(),
$promise = $deferred.promise(),
loadHandler = function ( response ) {
$deferred.resolve( response.text(), 'success', response );
},
errorHandler = function ( error ) {
$deferred.reject( error, 'error' );
};
fetch( url, {
method: 'GET',
mode : 'cors'
} )
.then( loadHandler )
.catch( errorHandler );
return $promise;
}
function $promise_gm_xmlhttprequest( url ) {
var $deferred = $.Deferred(),
$promise = $deferred.promise(),
loadHandler = function ( response ) {
$deferred.resolve( response.responseText, 'success', response );
},
errorHandler = function ( response ) {
$deferred.reject( response, 'error' );
};
GM_xmlhttpRequest( {
method : 'GET',
url : url,
onload : loadHandler,
onerror : errorHandler
} );
return $promise;
}
function $no_reject_wrapper( $orig_promise ) {
var $deferred = $.Deferred(),
$promise = $deferred.promise(),
doneHandler = function () {
//$deferred.resolveWith( $deferred, arguments ); // TODO: See [Issue #4040](https://github.com/jquery/jquery/issues/4040)
$deferred.resolve.call( $deferred, { arguments : arguments } );
};
$orig_promise
.always( doneHandler );
return $promise;
}
} )();
// end of CORS_TEST
@furyutei
Copy link
Author

furyutei commented Apr 17, 2018

きっかけ

furyutei/jsTwitterOAuth をいじっているときに、

久しぶりに本家Greasemonkeyを動かしてみたのだが、GM_xmlhttpReqeust()を使わなくても、jQuery.ajax()でクロスドメイン通信できるっぽい(Access-Control-Allow-Originヘッダ無しでも)。
元ツイート

ということに気付いたため。

@furyutei
Copy link
Author

テスト方法

上のユーザースクリプトをインストールして、テスト用ぺージを開く。
※結果は、開発者ツールのコンソールに出力

@furyutei
Copy link
Author

テストするアクセス方法

  1. XMLHttpRequest
  2. fetch
  3. GM_xmlhttpRequest / GM.xmlHttpRequest
  4. $.ajax (jQuery)
  5. $.ajax (トランスポートにGM_xmlhttpRequest使用)

@furyutei
Copy link
Author

furyutei commented Apr 17, 2018

テスト結果

  • Greasemonkey (4.3)
    1~5の全てでアクセス可能

  • Tampermonkey (4.6.5757) / Violentmonkey (2.9.1)
    3, 5 のみアクセス可能(1, 2, 4は不可)

@furyutei
Copy link
Author

furyutei commented Apr 17, 2018

備考

  • Tampermonkey / Violentmonkey では、CORS未対応の("Access-Control-Allow-Origin"が返らない/含まれない)リソースを GM_xmlhttpReqauest で取得するためには、@connect にて対象を明示する必要あり
  • Greasemonkey では、@connect の記述は意味がない(未サポート)
  • Greasemonkey 4.0+ では、GM_xmlhttpRequest の代わりに GM.xmlHttpRequest@grant すること(間違い探しか?)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment