Mozilla:如何使用nsITraceableChannel伪造响应

时间:2015-12-12 21:10:30

标签: proxy firefox-addon firefox-addon-sdk mozilla

我正在使用nsITraceableChannel(npm上的addon-proxy)开发代理。我可以修改传入的响应,但我如何一个?

假设网址包含hello.html,我想将hello world作为回复。我无法重定向到另一个页面,因为它不会对客户端透明,并且会混淆同源的东西。

现在我让请求转到服务器并完全重写传入的响应(请参阅Noitidart的代码片段,可以在许多变体中在线找到)。这远不是我特定用例的最佳选择:

  • 我发送请求并等待服务器,即使数据在本地可用。

  • 我无法从实际值更改内容类型和状态代码等标题。

有没有办法在不在网络上实际发布响应的情况下向底层侦听器提供完整的响应?

1 个答案:

答案 0 :(得分:0)

这绝对是可能的。

这个要点展示了如何获取它的副本: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