Mozilla: как подделать ответ с помощью nsITraceableChannel

Я разрабатываю прокси, используя nsITraceableChannel (addon-proxy в npm). Я могу изменять входящие ответы, но как мне их подделать?

Допустим, если URL-адрес содержит hello.html, я хочу вернуть hello world в качестве ответа. Я не могу перенаправить на другую страницу, потому что она не будет прозрачной для клиента и будет мешать работе с материалами того же происхождения.

А пока я позволяю запросу идти на сервер и просто полностью переписываю входящий ответ (см. Фрагмент Noitidart, который можно найти в Интернете во многих вариантах). Это далеко не оптимально для моего конкретного случая использования:

  • Я отправляю запрос и все равно жду сервера, даже если данные доступны локально.

  • Я не могу изменить заголовки, такие как тип содержимого и код состояния, с фактического значения.

Есть ли способ передать полный ответ базовому слушателю, не отправляя его в сети?


person BruceBerry    schedule 12.12.2015    source источник


Ответы (1)


Это абсолютно возможно.

Эта суть показывает, как просто получить его копию: https://gist.github.com/Noitidart/d0b08629ee2804538ad9 - вы можете скопировать и вставить это в блокнот, нажать «Выполнить» и увидеть в действии.

Обратите внимание на строку 44, она устанавливает this.responseBody = this.receivedChunks.join('');

Таким образом, вы могли бы установить this.responseBody на все, что захотите

Вот код из сути:

var {classes: Cc, interfaces: Ci, results: Cr, Constructor: CC, utils: Cu} = Components;
Cu.import('resource://gre/modules/Services.jsm');

var BinaryInputStream = CC('@mozilla.org/binaryinputstream;1', 'nsIBinaryInputStream', 'setInputStream');
var BinaryOutputStream = CC('@mozilla.org/binaryoutputstream;1', 'nsIBinaryOutputStream', 'setOutputStream');
var StorageStream = CC('@mozilla.org/storagestream;1', 'nsIStorageStream', 'init');

function TracingListener() {
    this.receivedChunks = []; // array for incoming data. holds chunks as they come, onStopRequest we join these junks to get the full source
    this.responseBody; // we'll set this to the 
    this.responseStatusCode;

    this.deferredDone = {
        promise: null,
        resolve: null,
        reject: null
    };
    this.deferredDone.promise = new Promise(function(resolve, reject) {
        this.resolve = resolve;
        this.reject = reject;
    }.bind(this.deferredDone));
    Object.freeze(this.deferredDone);
    this.promiseDone = this.deferredDone.promise;
}
TracingListener.prototype = {
    onDataAvailable: function(aRequest, aContext, aInputStream, aOffset, aCount) {
        var iStream = new BinaryInputStream(aInputStream) // binaryaInputStream
        var sStream = new StorageStream(8192, aCount); // storageStream // not sure why its 8192 but thats how eveyrone is doing it, we should ask why
        var oStream = new BinaryOutputStream(sStream.getOutputStream(0)); // binaryOutputStream

        // Copy received data as they come.
        var data = iStream.readBytes(aCount);
        this.receivedChunks.push(data);

        oStream.writeBytes(data, aCount);

        this.originalListener.onDataAvailable(aRequest, aContext, sStream.newInputStream(0), aOffset, aCount);
    },
    onStartRequest: function(aRequest, aContext) {
        this.originalListener.onStartRequest(aRequest, aContext);
    },
    onStopRequest: function(aRequest, aContext, aStatusCode) {
        this.responseBody = this.receivedChunks.join('');
        delete this.receivedChunks;
        this.responseStatus = aStatusCode;
        this.originalListener.onStopRequest(aRequest, aContext, aStatusCode);

        this.deferredDone.resolve();
    },
    QueryInterface: function(aIID) {
        if (aIID.equals(Ci.nsIStreamListener) || aIID.equals(Ci.nsISupports)) {
            return this;
        }
        throw Cr.NS_NOINTERFACE;
    }
};

var httpResponseObserver = {
    observe: function(aSubject, aTopic, aData) {
        var newListener = new TracingListener();
        aSubject.QueryInterface(Ci.nsITraceableChannel);
        newListener.originalListener = aSubject.setNewListener(newListener);
        /////// END - DO NOT EDIT
        newListener.promiseDone.then(
            function() {
                // no error happened
                console.log('yay response done:', newListener.responseBody);
            },
            function(aReason) {
                // promise was rejected, right now i didnt set up rejection, but i should listen to on abort or bade status code then reject maybe
            }
        ).catch(
            function(aCatch) {
                console.error('something went wrong, a typo by dev probably:', aCatch);
            }
        );
    }
};

Services.obs.addObserver(httpResponseObserver, 'http-on-examine-response', false);
// Services.obs.removeObserver(httpResponseObserver, 'http-on-examine-response'); // call this when you dont want to listen anymore
person Noitidart    schedule 13.12.2015
comment
Я уже этим занимаюсь (см. github.com/BruceBerry/addon -proxy / blob / master / proxy.js), и тому есть бесчисленное множество примеров. В отличие от обычного случая, когда вы хотите изменить реальный ответ от сервера, здесь я хочу вернуть полностью поддельный ответ, поэтому применяются # 1 и # 2: с вашим кодом вы отправляете запрос в сеть без причины, вы ожидают ответа, чтобы вернуть данные, даже если данные уже доступны локально, и, что наиболее важно, вы не можете изменить код состояния ответа, тип содержимого и другие значения. - person BruceBerry; 13.12.2015
comment
@BruceBerry Я уверен, что он не отправляет еще один запрос. Делает копию запроса. Если вы хотите подделать код статуса ответа, я не знаю, как это сделать. Что, я думаю, вам следует сделать, это что-то вроде того, что когда вы переходите на страницу, которая показывает страницу с проблемой загрузки, вы видите, как они перенаправляют ее на другую страницу, но URL-адрес не меняется? Посмотрите, как они это делают, и поделитесь, пожалуйста. Что я точно знаю, так это то, что новый URL-адрес отображается в nsiWebNavigation как - win.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIWebNavigation).document.documentURI; - person Noitidart; 14.12.2015
comment
Я посмотрю, спасибо. Мое требование - не избегать повторного запроса (как вы сказали, этого не происходит), но также избегать первого! Как только канал распознает, что этот запрос действительно может быть обслужен локально, он не должен отправлять какие-либо данные по сети и ждать ответа. - person BruceBerry; 14.12.2015
comment
@BruceBerry, вот информация о том, чем docUri отличается, но document.location является оригинальным - форумы .mozillazine.org / viewtopic.php? p = 13296131 # p13296131 - person Noitidart; 14.12.2015