Setting HTTP request headers

Cette traduction est incomplète. Aidez à traduire cet article depuis l'anglais.

Le protocole HTTP  est l'une des technologies les plus importantes du Web. Au-delà du contenu, d'autres informations importantes sont passées par l'intermédiaire des en-têtes HTTP à la fois pour les réponses et les requêtes.

Vous pouvez ajouter vos propres en-têtes HTTP a n'importe laquelle des requêtes faites par votre application, que ce soit explicitement par une requête XMLHttpRequest ou bien par une balise "img" dans votre code ou même depuis le CSS

HTTP Channels

Le travail sur les requêtes et réponses HTTP se fait généralement par l'interface nsIHttpChannel. Cette dernière possède de nombreuses méthodes et propriétés pour le traitement des requêtes et réponse, dont setRequestHeader qui nous intérèsse plus particulièrement ici. Cette méthode nous permet en effet de définir un en-tête de requête HTTP.

Ci-dessous, un exemple de définition d'en-tête 'X-Hello', valant 'World' :

// adds "X-Hello: World" header to the request
httpChannel.setRequestHeader("X-Hello", "World", false);

Dans cet exemple, la variable httpChannel est un objet implémentant l'interface nsIHttpChannel. (Bien qu'on eut pu choisir de la nommer différemment)

La méthode setRequestHeader prends 3 paramètres. Le premier comme vu dans l'exemple, est le nom de l'en-tête. Le second est la valeur associée à cet en-tête. Concernant le troisième paramètre, ignorez-le pour le moment et contentez-vous de le fixer à false.

NOTE: Pour des en-têtes personnalisés comme dans l'exemple ci-dessus, vous devrez rajouter 'X-' au nom de vôtre en-tête.


Plus besoin aujourd'hui: http://tools.ietf.org/html/rfc6648

Notifications

The question that may be coming to your mind right now is, how do you get the nsIHttpChannel when an HTTP request is made.

In the case your code initiates the request, you probably already have one. Trapping other requests is done with notifications, which are a lot like events or signals found in other languages and frameworks.

In particular, to get the nsIHttpChannel just before the HTTP request is made we need to observe the "http-on-modify-request" topic. (And yes, "http-on-modify-request" is a string.)

NOTE: There are many topics, besides just "http-on-modify-request", that you can get notifications about, for example "http-on-examine-response" and "xpcom-shutdown". You can also make up your own topics and send out your own notifications.

For more information about notifications framework and a list of common notification topics, see Observer Notifications.

Observers

To get notified about some topic (like "http-on-modify-request") we need to create an observer. An observer is a component implementing nsIObserver interface. And once the observer is registered for a topic, it will get notified about the topic by having its observe method called.

Below is an example observer that adds a custom header "X-Hello" to the channel passed in for http-on-modify-request notification:

var {Cc, Ci} = require("chrome");
var httpRequestObserver =
{
  observe: function(subject, topic, data) 
  {
    if (topic == "http-on-modify-request") {
      var httpChannel = subject.QueryInterface(Ci.nsIHttpChannel);
      httpChannel.setRequestHeader("X-Hello", "World", false);
    }
  }
};
NOTE: The above code was changed to reflect that in a FireFox Add-On one can not directly access Components.interfaces und Components.classes anymore but must use the require line introduced to the sample code above. So where you see Ci and Cc in this page there used to be Components.interfaces and Components.classes.
Please also note that the code itself usually is wrapped inside a exports.main = function() { ... } block.

Note that the number of parameter that the observe method takes is important. It takes 3 parameters (as we've shown in the example code above). For the "http-on-modify-request" topic, the first parameter (named subject in the code above) will be the nsIHttpChannel. However, it is passed to us as an nsISupports. So we need to change the nsISupports into a nsIHttpChannel which is what the QueryInterface call does. And yes, converting objects from one kind to another is very ugly, and lacks (what is usually called) syntactic sugar.

The second line of code in the if block should already be familiar to you. It is the same code we used before, earlier in this article, to add the HTTP request header.

The name of this object -- httpRequestObserver -- isn't important. We could have named it anything we liked.

Registering

After we've created the observer, we need to register it. In our case, we want to register it for the "http-on-modify-request" topic. We can do this with the code below.

var observerService = Cc["@mozilla.org/observer-service;1"]
                                .getService(Ci.nsIObserverService);
observerService.addObserver(httpRequestObserver, "http-on-modify-request", false);

The first statement gets the object that will let us register with topics that we want to get notified about.

The second statement does the actual registering. We are saying we want httpRequestObserver to be notified (by calling its observe method) when a "http-on-modify-request" topic takes place (which we know happens just before each HTTP request).

Unregistering

You should unregister the observer on shutdown. Failing to do that may cause memory leaks. To unregister the observer use nsIObserverService.removeObserver as follows:

observerService.removeObserver(httpRequestObserver, "http-on-modify-request");

All-in-one example

Here is a slightly different version of our httpRequestObserver object. While the previous version we showed before was good for learning, in an actual real-world application, you'd probably want to code it more like the following.

var httpRequestObserver =
{
  observe: function(subject, topic, data)
  {
    if (topic == "http-on-modify-request") {
      var httpChannel = subject.QueryInterface(Ci.nsIHttpChannel);
      httpChannel.setRequestHeader("X-Hello", "World", false);
    }
  },

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

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

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

This object has a convenience register() and unregister() methods, so in order to activate it you just need to call:

httpRequestObserver.register();

You should also remember to unregister the observer at shutdown:

httpRequestObserver.unregister();

And that's it.

XPCOM components

You need to register a single http-on-modify-request observer per application (and not one per window). This means that you should put the observer's implementation in an XPCOM component instead of an overlay. If you want to support Gecko2 (Firefox4) you need to register your javascript component as described here: https://developer.mozilla.org/en/XPCOM/XPCOM_changes_in_Gecko_2.0#JavaScript_components.

var headerName  = "X-hello";
var headerValue = "world";

function LOG(text)
{
    //    var consoleService = Components.classes["@mozilla.org/consoleservice;1"].getService(Components.interfaces.nsIConsoleService);
    //    consoleService.logStringMessage(text);
}

function myHTTPListener() { }

myHTTPListener.prototype = {
    
  observe: function(subject, topic, data)
  {
      if (topic == "http-on-modify-request") {

          LOG("----------------------------> (" + subject + ") mod request");

          var httpChannel = subject.QueryInterface(Components.interfaces.nsIHttpChannel);
          httpChannel.setRequestHeader(headerName, headerValue, false);
          return;
      }


      if (topic == "profile-after-change") {

          LOG("----------------------------> profile-after-change");

          var os = Components.classes["@mozilla.org/observer-service;1"]
                             .getService(Components.interfaces.nsIObserverService);

          os.addObserver(this, "http-on-modify-request", false);
          return;
      }
  },
 
  QueryInterface: function (iid) {
        if (iid.equals(Components.interfaces.nsIObserver) ||
            iid.equals(Components.interfaces.nsISupports))
            return this;
        
        Components.returnCode = Components.results.NS_ERROR_NO_INTERFACE;
        return null;
    },
};

var myModule = {
    registerSelf: function (compMgr, fileSpec, location, type) {

        var compMgr = compMgr.QueryInterface(Components.interfaces.nsIComponentRegistrar);
        compMgr.registerFactoryLocation(this.myCID,
                                        this.myName,
                                        this.myProgID,
                                        fileSpec,
                                        location,
                                        type);


          LOG("----------------------------> registerSelf");

        var catMgr = Components.classes["@mozilla.org/categorymanager;1"].getService(Components.interfaces.nsICategoryManager);
        catMgr.addCategoryEntry("app-startup", this.myName, this.myProgID, true, true);
    },


    getClassObject: function (compMgr, cid, iid) {

          LOG("----------------------------> getClassObject");

        return this.myFactory;
    },

    myCID: Components.ID("{9cf5f3df-2505-42dd-9094-c1631bd1be1c}"),

    myProgID: "@dougt/myHTTPListener;1",

    myName:   "Simple HTTP Listener",

    myFactory: {
        QueryInterface: function (aIID) {
            if (!aIID.equals(Components.interfaces.nsISupports) &&
                !aIID.equals(Components.interfaces.nsIFactory))
                throw Components.results.NS_ERROR_NO_INTERFACE;
            return this;
        },

        createInstance: function (outer, iid) {

          LOG("----------------------------> createInstance");

          return new myHTTPListener();
        }
    },

    canUnload: function(compMgr) {
        return true;
    }
};

function NSGetModule(compMgr, fileSpec) {
    return myModule;
}

Privacy and security good practice

A use case for setting specific a HTTP request header is to have a specific web site be able to check if a specific plugin / addon / extension is installed.

The good practice is not to have this specific HTTP header (for example "X-site.net-extension") sent all the time but only when doing requests with this specific web sites. By not advertising to all sites what extensions are installed this improves both privacy (this makes it harder to track a user known by his set of plugins, addons and extensions) and security (some plugins, addons and extensions may be known to have flaws by attackers).

With this privacy and security addition the code to use becomes:

observe: function(subject, topic, data)
 {
   if (topic == "http-on-modify-request") {
     var httpChannel = subject.QueryInterface(Ci.nsIHttpChannel);
     if (/site.net/.test(httpChannel.originalURI.host)) {
          httpChannel.setRequestHeader("X-Hello", "World", false);
     }
   }
 },

Étiquettes et contributeurs liés au document

 Contributeurs à cette page : LucasKostka
 Dernière mise à jour par : LucasKostka,