43

I've been trying at this for a long time now, and no good results.

var myObserver = {
    observe: function(subject, topic, data)
    {
        if (topic == "http-on-examine-response") 
        {   
             //  implement later
        } 
        else if(topic == "http-on-modify-request") 
        {
             //  implement later
        }
   },

   QueryInterface : function (id)
   {
       if (id.equals(Components.interfaces["nsIObserver"]) ||
           id.equals(Components.interfaces["nsISupports"]))
       {
           return this;
       }
       throw Components.results.NS_NOINTERFACE;
   }
};

var obs = new Service("observer-service", "ObserverService");
obs.addObserver(myObserver, "http-on-modify-request", false);

Basically, on http-on-modify-request, I know how to examine the URI, figure out which window (if any) it's associated with, and a bunch of other stuff. What I can't figure out is how to redirect a request, which I know is possible from here, because I can get an nsIHttpChannel before any request is ever sent out.

Anyone know what to do? :/ I've been trying for a couple of weeks on and off, and got nowhere.

Juan Cortés
  • 20,634
  • 8
  • 68
  • 91
Avindra Goolcharan
  • 4,032
  • 3
  • 41
  • 39
  • What do you mean by redirect a request? Redirecting the browser location to another URL? – Sam Ingrassia Feb 20 '10 at 05:48
  • 3
    Yes, but in the context of what I'm doing. I figured it out, I'll post the solution for others later if I get around to it. – Avindra Goolcharan Feb 22 '10 at 21:36
  • 2
    That solution would probable be helpful. – Tim Matthews Mar 15 '10 at 10:56
  • 3
    Do you happen to still have that solution lying around? It is nice to have answers on here marked as complete and everything, as well as being very useful for people finding this question through search engines. – SeriousSamP Nov 10 '11 at 16:02
  • could you please post your solution? – nonchip Dec 11 '11 at 12:49
  • 4
    Usin setRequestHeader, you can do that, simple: setRequestHeader('Location', 'http://newsite.com', true) https://developer.mozilla.org/en/Creating_Sandboxed_HTTP_Connections https://developer.mozilla.org/en/XPCOM_Interface_Reference/nsIHttpChannel#setResponseHeader() – StiveKnx Mar 08 '12 at 17:45
  • @AvindraGoolcharan, if you have the solution to this problem you should probably post it. Looks like some other users would like to see how its done. Thanks! – thomallen May 01 '12 at 22:57

4 Answers4

3

We can do this by overriding the nsiHttpChannel with a new one, doing this is slightly complicated but luckily the add-on https-everywhere implements this to force a https connection.

https-everywhere's source code is available here

Most of the code needed for this is in the files

[IO Util.js] [ChannelReplacement.js]

We can work with the above files alone provided we have the basic variables like Cc,Ci set up and the function xpcom_generateQI defined.

var httpRequestObserver =
{ 
  observe: function(subject, topic, data) {
    if (topic == "http-on-modify-request") {

        var httpChannel = subject.QueryInterface(Components.interfaces.nsIHttpChannel);     
        var requestURL = subject.URI.spec;

        if(isToBeReplaced(requestURL))  {

            var newURL = getURL(requestURL);        
             ChannelReplacement.runWhenPending(subject, function() {
                    var cr = new ChannelReplacement(subject, ch);
                    cr.replace(true,null);
                    cr.open();
                });
        }
    }

  },

  get observerService() {
    return Components.classes["@mozilla.org/observer-service;1"]
                     .getService(Components.interfaces.nsIObserverService);
  },

  register: function() {
    this.observerService.addObserver(this, "http-on-modify-request", false);

  },

  unregister: function() {
    this.observerService.removeObserver(this, "http-on-modify-request");

  }
};


httpRequestObserver.register();

The code will replace the request not redirect.

While I have tested the above code well enough, I am not sure about its implementation. As far I can make out, it copies all the attributes of the requested channel and sets them to the channel to be overridden. After which somehow the output requested by original request is supplied using the new channel.

P.S. I had seen a SO post in which this approach was suggested.

Daniel Marschall
  • 3,739
  • 2
  • 28
  • 67
mdprasadeng
  • 143
  • 1
  • 8
  • can you please provide some missing functions like isToBeReplace and ChannelReplacement – Noitidart Feb 26 '14 at 08:34
  • isToBeReplace is your logic which should decide whether to redirect or not. ChannelReplacement is defined in ChannelReplacement.js – mdprasadeng Feb 27 '14 at 07:01
  • Much thanks @mdprasadeng for the reply i was worried the message wouldn't get to you. isToBeReplace I can understand that much, but can you please link to ChannelReplacement.js the above links are broken – Noitidart Feb 27 '14 at 07:12
  • 1
    you noticied that the links are broken. I can't put my entire project so here are the links to those two files [ChannelReplacement.js](https://drive.google.com/file/d/0BwcgKCBJ8C8cUmk5ckl2WG0zN3c/edit?usp=sharing) [IOUtil.js](https://drive.google.com/file/d/0BwcgKCBJ8C8cRzlYaXlvVWxNbFk/edit?usp=sharing) – mdprasadeng Feb 27 '14 at 16:44
  • 1
    Unfortunately my project is for an ex employer and hence can't put the entire project online. If you are having trouble let me know and I will create a mini version of it. – mdprasadeng Feb 28 '14 at 18:37
  • Ah yes please can you kindly do that. It would super help me out. I can do redirect of channel but only with the ```redirectTo``` but that is only FF20+ compatabile. A sincere thank you for this help I like to help people too by creating mini demos! +1 for this :) – Noitidart Mar 01 '14 at 09:04
1

I am under the impression that you cannot do this at this level - I tried a variety of methods of externally "tricking" the code that calls for a nsIHttpChannel to be created (example at end of post).

What I would recommend is if you want a redirect, contact the channel's owner window (which works 99% of the time), and instruct it to redirect. I know that it's not going to behave the same, but since I don't know exactly why you're doing this, this will externally (to the user) appear to be doing the same thing as what you ask.

Here's the basics of what I was trying:

if(aTopic == "http-on-examine-response") {                                                                                     
            var request = aSubject.QueryInterface(Components.interfaces.nsIHttpChannel);                                                      

            if(!request.URI.spec.match("^http://www.apple.com/")) {                                                          
                var ios = Components.classes["@mozilla.org/network/io-service;1"]                                                             
                    .getService(Components.interfaces.nsIIOService);                                                                          
                var ch = ios.newChannel("http://www.apple.com/", null, null);                                                                 

                var listener = {                                                                                                              
                    QueryInterface : XPCOMUtils.generateQI([Ci.nsIChannelEventSink]),                                                         
                    onDataAvailable: function() {},                                                                                           
                    onStopRequest: function() {},                                                                                             
                    onStartRequest: function() {}                                                                                             
                };                                                                                                                            

                ch.asyncOpen(listener,null);                                                                                                  

                var eventSink = request.notificationCallbacks.getInterface(Ci.nsIChannelEventSink);                                           
                eventSink.asyncOnChannelRedirect(request,ch,Ci.nsIChannelEventSink.REDIRECT_INTERNAL,function() {});                          
            } 
Daniel
  • 1,188
  • 11
  • 22
0

I've done it this way: stop nsIHttpChannel on "http-on-modify-request" event, get browser object for current window, call browser.loadURI.

var utils = require("sdk/window/utils");

function needsRedirect(url) {
    // to be implemented
    return true;
}

function generateNewUrl(url) {
    // to be implemented
    return "http://www.example.com/";
}

Cc["@mozilla.org/observer-service;1"]
    .getService(Ci.nsIObserverService)
    .addObserver({
        observe: function(subject, topic, data) {
            var channel = subject.QueryInterface(Ci.nsIHttpChannel);
            var url = channel.originalURI.spec;
            if (needsRedirect(url)) {
                //stop
                channel.cancel(Cr.NS_BINDING_ABORTED);

                //redirect
                var gBrowser = utils.getMostRecentBrowserWindow().gBrowser;
                var domWin = channel.notificationCallbacks.getInterface(Ci.nsIDOMWindow);
                var browser = gBrowser.getBrowserForDocument(domWin.top.document);
                browser.loadURI(generateNewUrl(url));

            }
        }
    }, "http-on-modify-request", false);
Daniel Marschall
  • 3,739
  • 2
  • 28
  • 67
vadimrostok
  • 131
  • 7
  • 1
    This code seems not to work as expected. When I apply this code to a website which has images in it, this call will be made to the image, and so the current loaded page will be replaced with the URL generateNewUrl("image.jpg") instead of generateNewUrl("index.html") . I think `channel.redirectTo(nsUrl)` is a better way. – Daniel Marschall Jun 19 '14 at 22:27
0

While testing something I created a condensed version (see this gist) of the channel replacement logic mentioned in other answers.

The general idea seems to be to transfer all critical properties over to the new channel, remove callbacks from the old channel so manipulations won't trip up the page load and then shutting the old channel down.

With some modifications one can change the page URI for document loads or leave it as-is.

Warning: That was just a quick hack to get a few pages loading, I have not tested it in depth and it will probably break for some cases. I suspect there are reasons why the HTTPS Everywhere is more complex.

the8472
  • 40,999
  • 5
  • 70
  • 122