Proxy Mozilla:如何使用nsITraceableChannel伪造响应

Proxy Mozilla:如何使用nsITraceableChannel伪造响应,proxy,firefox-addon,firefox-addon-sdk,mozilla,Proxy,Firefox Addon,Firefox Addon Sdk,Mozilla,我正在使用npm上的nsiTracableChannel(addon proxy)开发一个代理。我可以修改传入的响应,但如何伪造响应 假设URL包含hello.html,我想返回hello world作为响应。我不能重定向到另一个页面,因为它对客户端不透明,并且会弄乱相同来源的内容 现在,我让请求转到服务器,然后完全重写传入的响应(请参阅Noitidart的代码片段,它可以在许多变体中在线找到)。对于我的特定用例来说,这远远不是最佳的: 我发送请求并等待服务器,即使数据在本地可用 我无法从实际

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

假设URL包含
hello.html
,我想返回
hello world
作为响应。我不能重定向到另一个页面,因为它对客户端不透明,并且会弄乱相同来源的内容

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

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

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


有没有一种方法可以向底层侦听器提供完整的响应而不在网络上发出响应?

这是绝对可能的

这个要点展示了如何获取它的副本:-您可以将它复制并粘贴到scratchpad,点击run,然后查看它的运行情况

注意在第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

这已经是我正在做的事情(见),并且有无数的例子。与您希望修改来自服务器的真实响应的正常情况不同,这里我想返回一个完全虚假的响应,因此#1和#2适用:使用代码,您在网络上无故发送请求,您正在等待响应以返回数据,即使数据在本地已经可用,而且最重要的是,您不能更改响应状态代码、内容类型和其他值。@BruceBerry我确定它没有发送其他请求。它正在复制一个请求。如果你想伪造一个响应状态码,我不知道该怎么做。我认为您应该做的是,当您转到显示加载页面有问题的页面时,您会看到他们如何将其重定向到另一个页面,但url不会更改?看看他们是怎么做到的,请分享。我所知道的是,新的url出现在
nsiWebNavigation
中,类似于-
win.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsiWebNavigation).document.documentURI我来看看,谢谢。我的要求不是避免提出第二个请求(正如你所说,它不会),而是避免第一个请求!一旦通道确认此请求实际上可以在本地提供,它就不应该通过网络发送任何数据并等待响应。@BruceBerry这里是关于docUri有何不同的信息,但document.location是原始的-