mozilla

Revision 65496 of Setting HTTP request headers

  • Revision slug: Setting_HTTP_request_headers
  • Revision title: Setting HTTP request headers
  • Revision id: 65496
  • Created:
  • Creator: sjltaylor
  • Is current revision? No
  • Comment 24 words added

Revision Content

HTTP is one of the core technologies behind the Web. In addition to the actual content, some important information is passed with HTTP headers for both HTTP requests and responses.

You can add your own HTTP headers to any request the application makes, whether the request is initiated by your code explicitly opening an HTTP channel, because of XMLHttpRequest activity, an <img> element in content, or even from CSS.

HTTP Channels

When you deal with HTTP requests and responses, typically you are doing this with an nsIHttpChannel. The nsIHttpChannel interface has a number of properties and methods, but the method that is of interest to us is setRequestHeader. This method allows us to set an HTTP request header.

Below is some sample code where we set an HTTP header.

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

In the example code above we have a variable named httpChannel which points to an object implementing nsIHttpChannel. (This variable could have been named anything though.)

The setRequestHeader method takes 3 parameters. The first parameter is the name of the HTTP request header. The second parameter is the value of the HTTP request header. And for now, just ignore the third parameter, and just always make it false.

In our example code, the HTTP request header that we added is named X-Hello and the value of this HTTP request header is World.

NOTE: If you are making up your own HTTP header, you MUST put a X- in front of the name. (In our example, our made up HTTP header is X-Hello and NOT Hello because we correctly added the X- in front of our name.)


No longer the case: http://tools.ietf.org/html/draft-ietf-appsawg-xdash-02

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 httpRequestObserver =
{
  observe: function(subject, topic, data) 
  {
    if (topic == "http-on-modify-request") {
      var httpChannel = subject.QueryInterface(Components.interfaces.nsIHttpChannel);
      httpChannel.setRequestHeader("X-Hello", "World", false);
    }
  }
};

div class="note"> Doesn't seem very suitable for this article; readers should are typically assumed to be familiar with JS. Nickolay '''NOTE''': Often people expect JavaScript to be just like Java. And while superficially, they look very similar, there are some important differences between the two. For example, while Java is an <em>object-oriented programming language</em>, JavaScript is not. JavaScript is <em>prototype-based programming language</em> and as such while it has <em>objects</em> it does not have <em>classes</em>. (Which is why, if you are not well versed with JavaScript, the object creation in the sample code above may look strange.) </div

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 = Components.classes["@mozilla.org/observer-service;1"]
                                .getService(Components.interfaces.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(Components.interfaces.nsIHttpChannel);
      httpChannel.setRequestHeader("X-Hello", "World", false);
    }
  },

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

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(Components.interfaces.nsIHttpChannel);
      if (/site.net/.test(httpChannel.originalURI.host)) {
        httpChannel.setRequestHeader("X-Hello", "World", false);
      }
    }
  },

 

{{ languages( { "ja": "ja/Setting_HTTP_request_headers" } ) }}

Revision Source

<p><a href="/en/HTTP" title="en/HTTP">HTTP</a> is one of the core technologies behind the Web. In addition to the actual content, <a href="/en/HTTP/Headers" title="en/HTTP/Headers">some important information</a> is passed with HTTP headers for both HTTP requests and responses.</p>
<p>You can add your own HTTP headers to any request the application makes, whether the request is initiated by your code explicitly opening an HTTP channel, because of <a href="/en/nsIXMLHttpRequest" title="en/XMLHttpRequest">XMLHttpRequest</a> activity, an <a href="/En/HTML/Element/Img" title="En/HTML/Element/Img">&lt;img&gt;</a> element in content, or even from <a href="/en/CSS" title="en/CSS">CSS</a>.</p>
<h3 id="HTTP_Channels">HTTP Channels</h3>
<p>When you deal with HTTP requests and responses, typically you are doing this with an <code><a href="/en/XPCOM_Interface_Reference/nsIHttpChannel" title="en/XPCOM_Interface_Reference/nsIHttpChannel">nsIHttpChannel</a></code>. The <code>nsIHttpChannel</code> interface has a number of properties and methods, but the method that is of interest to us is <code>setRequestHeader</code>. This method allows us to <em>set an HTTP request header</em>.</p>
<p>Below is some sample code where we set an HTTP header.</p>
<pre class="eval">// adds "X-Hello: World" header to the request
httpChannel.setRequestHeader("X-Hello", "World", false);
</pre>
<p>In the example code above we have a variable named <code>httpChannel</code> which points to an object implementing <code>nsIHttpChannel</code>. (This variable could have been named anything though.)</p>
<p>The <code>setRequestHeader</code> method takes 3 parameters. The first parameter is the <em>name</em> of the HTTP request header. The second parameter is the <em>value</em> of the HTTP request header. And for now, just ignore the third parameter, and just always make it <code>false</code>.</p>
<p>In our example code, the HTTP request header that we added is <em>named</em> <code>X-Hello</code> and the <em>value</em> of this HTTP request header is <code>World</code>.</p>
<div class="note"> <p><strike><strong>NOTE</strong>: If you are making up your own HTTP header, you MUST put a <code>X-</code> in front of the name. (In our example, our made up HTTP header is <code>X-Hello</code> and NOT <code>Hello</code> because we correctly added the <code>X-</code> in front of our name.)</strike></p> <p><br> <strong>No longer the case: <a class=" external" href="http://tools.ietf.org/html/draft-ietf-appsawg-xdash-02" title="http://tools.ietf.org/html/draft-ietf-appsawg-xdash-02">http://tools.ietf.org/html/draft-ietf-appsawg-xdash-02</a></strong></p>
</div>
<h3 id="Notifications" name="Notifications">Notifications</h3>
<p>The question that may be coming to your mind right now is, how do you get the <code>nsIHttpChannel</code> when an HTTP request is made.</p>
<p>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 <em>events</em> or <em>signals</em> found in other languages and frameworks.</p>
<p>In particular, to get the <code>nsIHttpChannel</code> just before the HTTP request is made we need to <em>observe</em> the <code>"http-on-modify-request"</code> topic. (And yes, <code>"http-on-modify-request"</code> is a string.)</p>
<div class="note"> <p><strong>NOTE</strong>: There are many topics, besides just <code>"http-on-modify-request"</code>, that you can get notifications about, for example <code>"http-on-examine-response"</code> and <code>"xpcom-shutdown"</code>. You can also make up your own topics and send out your own notifications.</p> <p>For more information about notifications framework and a list of common notification topics, see <a href="/en/Observer_Notifications" title="en/Observer_Notifications">Observer Notifications</a>.</p>
</div>
<h3 id="Observers" name="Observers">Observers</h3>
<p>To get notified about some topic (like <code>"http-on-modify-request"</code>) we need to create an <strong>observer</strong>. An observer is a component implementing <a href="/en/XPCOM_Interface_Reference/nsIObserver" title="en/nsIObserver">nsIObserver</a> interface. And once the observer is <em>registered</em> for a topic, it will get notified about the topic by having its <code>observe</code> method called.</p>
<p>Below is an example observer that adds a custom header "X-Hello" to the channel passed in for http-on-modify-request notification:</p>
<pre class="brush: js">var httpRequestObserver =
{
  observe: function(subject, topic, data) 
  {
    if (topic == "http-on-modify-request") {
      var httpChannel = subject.QueryInterface(Components.interfaces.nsIHttpChannel);
      httpChannel.setRequestHeader("X-Hello", "World", false);
    }
  }
};
</pre>
<p><span class="comment">div class="note"&gt; Doesn't seem very suitable for this article; readers should are typically assumed to be familiar with JS. Nickolay '''NOTE''': Often people expect <a href="/en/JavaScript" title="en/JavaScript">JavaScript</a> to be just like <a href="/en/Java" title="en/Java">Java</a>. And while superficially, they look very similar, there are some important differences between the two. For example, while Java is an &lt;em&gt;object-oriented programming language&lt;/em&gt;, JavaScript is not. JavaScript is &lt;em&gt;prototype-based programming language&lt;/em&gt; and as such while it has &lt;em&gt;objects&lt;/em&gt; it does not have &lt;em&gt;classes&lt;/em&gt;. (Which is why, if you are not well versed with JavaScript, the object creation in the sample code above may look strange.) &lt;/div</span></p>
<p>Note that the number of parameter that the <code>observe</code> method takes is important. It takes 3 parameters (as we've shown in the example code above). For the <code>"http-on-modify-request"</code> topic, the first parameter (named <code>subject</code> in the code above) will be the <code>nsIHttpChannel</code>. However, it is passed to us as an <code><a href="/en/XPCOM_Interface_Reference/nsISupports" title="en/nsISupports">nsISupports</a></code>. So we need to <em>change</em> the <code>nsISupports</code> into a <code>nsIHttpChannel</code> which is what the <code>QueryInterface</code> call does. And yes, <em>converting</em> objects from one kind to another is very ugly, and lacks (what is usually called) <em>syntactic sugar</em>.</p>
<p>The second line of code in the <code>if</code> 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.</p>
<p>The name of this object -- <code>httpRequestObserver</code> -- isn't important. We could have named it anything we liked.</p>
<h3 id="Registering" name="Registering">Registering</h3>
<p>After we've created the observer, we need to register it. In our case, we want to register it for the <code>"http-on-modify-request"</code> topic. We can do this with the code below.</p>
<pre class="eval">var observerService = Components.classes["@<a class="linkification-ext external" href="http://mozilla.org/observer-service;1" title="Linkification: http://mozilla.org/observer-service;1">mozilla.org/observer-service;1</a>"]
                                .getService(Components.interfaces.<a href="/en/XPCOM_Interface_Reference/nsIObserverService" title="en/XPCOM_Interface_Reference/nsIObserverService">nsIObserverService</a>);
observerService.addObserver(httpRequestObserver, "http-on-modify-request", false);
</pre>
<p>The first statement gets the object that will let us register with topics that we want to get notified about.</p>
<p>The second statement does the actual registering. We are saying we want <code>httpRequestObserver</code> to be notified (by calling its <code>observe</code> method) when a <code>"http-on-modify-request"</code> topic takes place (which we know happens just before each HTTP request).</p>
<h3 id="Unregistering" name="Unregistering">Unregistering</h3>
<p>You should unregister the observer on shutdown. Failing to do that may cause memory leaks. To unregister the observer use <code>nsIObserverService.removeObserver</code> as follows:</p>
<pre class="eval">observerService.removeObserver(httpRequestObserver, "http-on-modify-request");</pre>
<h3 id="All-in-one_example">All-in-one example</h3>
<p>Here is a slightly different version of our <code>httpRequestObserver</code> 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.</p>
<pre class="brush: js">var httpRequestObserver =
{
  observe: function(subject, topic, data)
  {
    if (topic == "http-on-modify-request") {
      var httpChannel = subject.QueryInterface(Components.interfaces.nsIHttpChannel);
      httpChannel.setRequestHeader("X-Hello", "World", false);
    }
  },

  get observerService() {
    return Components.classes["@<a class="linkification-ext" href="http://mozilla.org/observer-service;1" title="Linkification: http://mozilla.org/observer-service;1">mozilla.org/observer-service;1</a>"]
                     .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");
  }
};
</pre>
<p>This object has a convenience <code>register()</code> and <code>unregister()</code> methods, so in order to activate it you just need to call:</p>
<pre class="eval">httpRequestObserver.register();
</pre>
<p>You should also remember to unregister the observer at shutdown:</p>
<pre class="eval">httpRequestObserver.unregister();
</pre>
<p>And that's it.<br> </p>
<h3 id="XPCOM_components" name="XPCOM_components">XPCOM components</h3>
<p>You need to register a single <code>http-on-modify-request</code> observer per application (and not one per window). This means that you should put the observer's implementation in an <a href="/en/How_to_Build_an_XPCOM_Component_in_Javascript" title="en/How_to_Build_an_XPCOM_Component_in_Javascript">XPCOM component</a> instead of an <a href="/en/XUL_Overlays" title="en/XUL_Overlays">overlay</a>. If you want to support Gecko2 (Firefox4) you need to register your javascript component as described here: <a class="linkification-ext" href="/en/XPCOM/XPCOM_changes_in_Gecko_2.0#JavaScript_components" title="Linkification: https://developer.mozilla.org/en/XPCOM/XPCOM_changes_in_Gecko_2.0#JavaScript_components">https://developer.mozilla.org/en/XPCOM/XPCOM_changes_in_Gecko_2.0#JavaScript_components</a>.</p>
<pre class="brush: js">var headerName  = "X-hello";
var headerValue = "world";

function LOG(text)
{
    //    var consoleService = Components.classes["@<a class="linkification-ext" href="http://mozilla.org/consoleservice;1" title="Linkification: http://mozilla.org/consoleservice;1">mozilla.org/consoleservice;1</a>"].getService(Components.interfaces.nsIConsoleService);
    //    consoleService.logStringMessage(text);
}

function myHTTPListener() { }

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

          LOG("----------------------------&gt; (" + subject + ") mod request");

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


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

          LOG("----------------------------&gt; profile-after-change");

          var os = Components.classes["@<a class="linkification-ext" href="http://mozilla.org/observer-service;1" title="Linkification: http://mozilla.org/observer-service;1">mozilla.org/observer-service;1</a>"]
                             .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("----------------------------&gt; registerSelf");

        var catMgr = Components.classes["@<a class="linkification-ext" href="http://mozilla.org/categorymanager;1" title="Linkification: http://mozilla.org/categorymanager;1">mozilla.org/categorymanager;1</a>"].getService(Components.interfaces.nsICategoryManager);
        catMgr.addCategoryEntry("app-startup", this.myName, this.myProgID, true, true);
    },


    getClassObject: function (compMgr, cid, iid) {

          LOG("----------------------------&gt; 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) &amp;&amp;
                !aIID.equals(Components.interfaces.nsIFactory))
                throw Components.results.NS_ERROR_NO_INTERFACE;
            return this;
        },

        createInstance: function (outer, iid) {

          LOG("----------------------------&gt; createInstance");

          return new myHTTPListener();
        }
    },

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

function NSGetModule(compMgr, fileSpec) {
    return myModule;
}
</pre>
<h3 id="Privacy_and_security_good_practice">Privacy and security good practice</h3>
<p>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.</p>
<p>The good practice is not to have this specific HTTP header (<code>for example </code>"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).</p>
<p>With this privacy and security addition the code to use becomes:</p>
<pre class="brush: js"> observe: function(subject, topic, data)
  {
    if (topic == "http-on-modify-request") {
      var httpChannel = subject.QueryInterface(Components.interfaces.nsIHttpChannel);
<code>      if (/site.net/.test(</code>httpChannel<code>.originalURI.host)) {
</code>        httpChannel.setRequestHeader("X-Hello", "World", false);
      }
    }
  },
</pre>
<p> </p>
<p>{{ languages( { "ja": "ja/Setting_HTTP_request_headers" } ) }}</p>
Revert to this revision