Firefox扩展:取消请求并发出假响应

Firefox extension: Cancel requests and emit fake responses

本文关键字:响应 并发 请求 扩展 取消 Firefox      更新时间:2023-09-26

我正在尝试开发一个Firefox扩展,该扩展可以将每个HTTP请求丢弃到某个站点并返回假响应。没有请求应该通过原来的web服务器,但我希望能够创建一个自定义响应。我试图拦截"http-on-modify-request"消息,但取消请求似乎不起作用,因为我无法模拟真实的响应之后。类似地,使用nsITraceableStream实例,我似乎无法真正取消请求。我想不出主意了,有人能帮帮忙吗?

下面的答案在Firefox 21中已经被取代,现在nsIHttpChannel.redirectTo()方法可以很好地完成这项工作。您可以重定向到data: URI,像这样可以:

Components.utils.import("resource://gre/modules/Services.jsm");
const Ci = Components.interfaces;
[...]
onModifyRequest: function(channel)
{
  if (channel instanceof Ci.nsIHttpChannel && shouldRedirect(channel.URI.spec))
  {
    let redirectURL = "data:text/html," + encodeURIComponent("<html>Hi there!</html>");
    channel.redirectTo(Services.io.newURI(redirectURI, null, null));
  }
}

原始答案(过时)

每个通道都有其关联的流侦听器,当接收到数据时,会收到通知。获取响应所需要做的就是获取此侦听器并向其提供错误的数据。nsITraceableChannel就是这样做的。您需要用您自己的不做任何事情的侦听器替换通道的常用侦听器,然后您可以取消通道而不通知侦听器。然后你触发监听器,给它你自己的数据。像这样:

Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
const Cc = Components.classes;
const Ci = Components.interfaces;
[...]
onModifyRequest: function(channel)
{
  if (channel instanceof Ci.nsIHttpChannel && channel instanceof Ci.nsITraceableChannel)
  {
    // Our own listener for the channel
    var fakeListener = {
      QueryInterface: XPCOMUtils.generateQI([Ci.nsIStreamListener,
                        Ci.nsIRequestObserver, Ci.nsIRunnable]),
      oldListener: null,
      run: function()
      {
        // Replace old listener by our fake listener
        this.oldListener = channel.setNewListener(this);
        // Now we can cancel the channel, listener old won't notice
        //channel.cancel(Components.results.NS_BINDING_ABORTED);
      },
      onDataAvailable: function(){},
      onStartRequest: function(){},
      onStopRequest: function(request, context, status)
      {
        // Call old listener with our data and set "response" headers
        var stream = Cc["@mozilla.org/io/string-input-stream;1"]
                       .createInstance(Ci.nsIStringInputStream);
        stream.setData("<html>Hi there!</html>", -1);
        this.oldListener.onStartRequest(channel, context);
        channel.setResponseHeader("Refresh", "5; url=http://google.com/", false);
        this.oldListener.onDataAvailable(channel, context, stream, 0, stream.available());
        this.oldListener.onStopRequest(channel, context, Components.results.NS_OK);
      }
    }
    // We cannot replace the listener right now, see
    // https://bugzilla.mozilla.org/show_bug.cgi?id=646370.
    // Do it asynchronously instead.
    var threadManager = Cc["@mozilla.org/thread-manager;1"]
                          .getService(Ci.nsIThreadManager);
    threadManager.currentThread.dispatch(fakeListener, Ci.nsIEventTarget.DISPATCH_NORMAL);
  }
}

这段代码的问题仍然是,如果频道被取消,页面显示空白(所以我注释了那一行)—似乎侦听器仍然查看频道并注意到它被取消了。