MDN wants to learn about developers like you: https://qsurvey.mozilla.com/s3/MDN-dev-survey

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

La question qui pourrait vous venir à l'esprit en ce moment est, comment obtenez-vous nsIHttpChannel Lorsqu'une demande HTTP est effectuée.

Dans le cas où votre code déclenche la demande, vous en avez déjà une. Le piégeage d'autres demandes se fait avec des notifications, qui ressemblent beaucoup à des événements ou des signaux trouvés dans d'autres langues et cadres.

En particulier, pour obtenir le nsIHttpChannel juste avant que la requête HTTP ne soit faite, nous devons observer le sujet "http-on-modify-request". (Et oui, "http-on-modify-request" est une chaîne.)

NOTE: Il existe de nombreux sujets, en plus simplement "http-on-modify-request", sur lequel vous pouvez recevoir des notifications, par exemple "http-on-examine-response" et "xpcom-shutdown". Vous pouvez également composer vos propres sujets et envoyer vos propres notifications.

Pour plus d'informations sur le cadre de notifications et une liste de sujets de notification communs, voir Observer Notifications.

Observers

Pour être informé de certains sujets (comme "http-on-modify-request"), nous devons créer un observateur. Un observateur est un composant implémentant nsIObserver interface. Et une fois que l'observateur est inscrit pour un sujet, il sera informé du sujet en appelant sa méthode d'observation.

Voici un exemple d'observateur qui ajoute un en-tête personnalisé "X-Hello" au canal transmis pour la notification http-on-modify-request:

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: Le code ci-dessus a été modifié afin de refléter que dans un complément FireFox ne peut pas accéder directement à Components.interfaces und Components.classes mais doit utiliser la ligne requise présentée dans l'exemple de code ci-dessus. Donc, où vous voyez Ci et Cc dans cette page, il s'agissait d'Components.interfaces et Components.classes.
Notez également que le code lui-même est habituellement enveloppé dans un hangar export.main = function () {...}.

Notez que le nombre de paramètres que prend la méthode d'observation est important. Il faut 3 paramètres (comme nous l'avons montré dans le code d'exemple ci-dessus). Pour le sujet "http-on-modify-request", le premier paramètre (sujet nommé dans le code ci-dessus) sera le nsIHttpChannel. Cependant, il nous est transmis en tant que un nsISupports. Nous devons donc changer les nsISupports en un nsIHttpChannel qui est ce que fait l'appel QueryInterface. Et oui, la conversion d'objets d'un genre à l'autre est très moche et manque (ce qu'on appelle habituellement) de sucre syntaxique.

La deuxième ligne de code dans le bloc if devrait déjà vous être familier. C'est le même code que nous avons utilisé avant, plus tôt dans cet article, pour ajouter l'en-tête de requête HTTP.

Le nom ce object  -- httpRequestObserver -- n'est pas important. Vous pouvez le normé comme vous le souhaitez.

Enregistrement

Après avoir créé l'observateur, nous devons l'enregistrer. Dans notre cas, nous voulons l'enregistrer pour le sujet "http-on-modify-request". Nous pouvons le faire avec le code ci-dessous.

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

La première déclaration obtient l'objet qui nous permettra de nous inscrire à des sujets sur lesquels nous voulons être informés.

La deuxième déclaration fait l'enregistrement réel. Nous disons que nous voulons que le serveur httpRequestObserver soit notifié (en appelant sa méthode d'observation) lorsqu'un sujet "http-on-modify-request" a lieu (ce qui nous arrive juste avant chaque demande HTTP).

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 : afidosstar, LucasKostka
 Dernière mise à jour par : afidosstar,