Firefox扩展:取消请求并发出虚假回复

时间:2011-08-28 17:25:26

标签: javascript http firefox firefox-addon xpcom

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

1 个答案:

答案 0 :(得分:9)

从Firefox 21开始,下面的答案已被取代,现在nsIHttpChannel.redirectTo() method可以很好地完成工作。您可以重定向到数据: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));
  }
}

原始答案(过时)

每个频道都有关联的stream listener,在收到数据时会收到通知。你需要做的就是伪造一个响应就是获得这个监听器并用错误的数据提供它。而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);
  }
}

此代码的问题仍然是如果频道被取消,页面显示为空白(所以我注释了该行) - 似乎听众仍然看着频道并注意到它已被取消。