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

发布于 2024-12-02 02:24:17 字数 206 浏览 1 评论 0 原文

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

I am trying to develop a Firefox extension that drops every HTTP request to a certain site and returns a fake response. No request should get through to the original web server, but I want to be able to create a custom response. I tried to intercept the "http-on-modify-request" message, but cancelling the request doesn't seem to work, as I cannot simulate a real response afterwards. Similarly, using an nsITraceableStream instance, I don't seem to be able to really cancel the request. I am out of ideas, can somebody help?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

孤独岁月 2024-12-09 02:24:17

从 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);
  }
}

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

The answer below has been superseded as of Firefox 21, now the nsIHttpChannel.redirectTo() method does the job nicely. You can redirect to a data: URI, something like this will work:

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));
  }
}

Original answer (outdated)

Each channel has its associated stream listener that gets notified when data is received. All you need to do to fake a response is to get this listener and feed it with wrong data. And nsITraceableChannel is in fact the way to do it. You need to replace the channel's usual listener by your own that won't do anything, after that you can cancel the channel without the listener being notified about it. And then you trigger the listener and give it your own data. Something like this:

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);
  }
}

The problem with this code is still that the page shows up blank if the channel is canceled (so I commented that line) - it seems that the listener still looks at the channel and notices that it is canceled.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文