Можно ли имея возможность изменить (inject) XMLHttpRequest изменить его так, чтобы можно было перехватить содержимое ответа не подменяя целиком весь объект со всеми его методами ?
Предполагается, что далее в коде будут вызовы вроде:
x = new XMLHttpRequest();
x.open( "GET", "/" );
x.onreadystatechange = function () {...};
x.send( null );
или даже с учетом однопоточности:
x = new XMLHttpRequest();
x.open( "GET", "/" );
x.send( null );
x.onreadystatechange = function () {...};
Ответ
Решение для Blink
Blink - это Chrome, Opera etc.
В прототипе XMLHttpRequest onreadystatechange объявлен как геттер/сеттер и задавая свой геттер/сеттер нужно не сломать логику работы XMLHttpRequest:
var oldXMLHttpRequest = XMLHttpRequest;
XMLHttpRequest = function() {
var xhr = new oldXMLHttpRequest();
// получаем дескриптор прототипных setter/getter
var descrGetSet = Object.getOwnPropertyDescriptor(
Object.getPrototypeOf( xhr ),
"onreadystatechange" );
var newSet = function( val ) {
console.log ( "setter" );
descrGetSet.set.call( xhr, function() { // прототипный setter
console.log( "Inject "+this.status ); // this.responseText
return val.apply( xhr, arguments );
} );
}
Object.defineProperty( xhr, "onreadystatechange", {
set: newSet, // новый setter
get: descrGetSet.get // старый getter
} );
return xhr;
}
Теперь сделав:
x = new XMLHttpRequest();
x.open( "GET", "/" );
x.send( null )
x.onreadystatechange = function () { console.log( this.statusText+' is 2' )};
Увидим
Inject 200
OK is 2
Inject 200
OK is 2
Inject 200
OK is 2
Что и должно быть при смене readyState
Решение для Webkit
Webkit - Safari, PhantomJS
Проблема в этом движке- onreadystatechange это геттер/сеттер объекта XHR, а не XHR.prototype. В итоге переопределить его нельзя. Унаследоваться от XHR тоже нельзя, поэтому нужно применять обертку для XHR
Весьма хороша эта обертка:
https://github.com/ilinsky/xmlhttprequest
Комментариев нет:
Отправить комментарий