aboutsummaryrefslogtreecommitdiff
path: root/files/zh-cn/mozilla/tech/xpcom/setting_http_request_headers/index.html
blob: b91030b424bda73688d4f59cb51479d1b45cf795 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
---
title: Setting HTTP request headers
slug: Mozilla/Tech/XPCOM/Setting_HTTP_request_headers
translation_of: Mozilla/Tech/XPCOM/Setting_HTTP_request_headers
---
<p><a href="https://developer.mozilla.org/en-US/docs/HTTP">HTTP</a> 是网络背后的核心技术之一。除了实质内容之外,一些重要的信息通过HTTP 头传递给HTTP 请求和响应。</p>

<p>你可以添加你自己的HTTP头到应用程序所做的任何请求中,不论你的代码是否启动了显式打开了基于 <a href="/zh-cn/nsIXMLHttpRequest" title="zh-cn/XMLHttpRequest">XMLHttpRequest</a> 活动、&lt;img&gt;元素或者甚至是从css中的 HTTP Channel的请求。</p>

<h3 id="HTTP_Channels">HTTP Channels</h3>

<p>当你处理HTTP请求和响应时,通常情况下你是通过 <code><a href="/zh-cn/XPCOM_Interface_Reference/nsIHttpChannel" title="zh-cn/XPCOM_Interface_Reference/nsIHttpChannel">nsIHttpChannel</a></code>来做的。 <code><a href="/zh-cn/XPCOM_Interface_Reference/nsIHttpChannel" title="zh-cn/XPCOM_Interface_Reference/nsIHttpChannel">nsIHttpChannel</a></code>接口有一些属性和方法,但是这下方法中我们只对<code>setRequestHeader感兴趣。这个方法允许我们设置一个</code> <em>HTTP request header</em>.</p>

<p>下面是设置一个HTTP header 的简单代码</p>

<pre class="eval">// adds "X-Hello: World" header to the request
httpChannel.setRequestHeader("X-Hello", "World", false);
</pre>

<p>在这个示例代码中我们有一个变量名为httpChannel,它是一个nsIHttpChannel的实例。</p>

<p>setRequestHeader有三个参数,第一个参数是HTTP请求头的名字,第二个参数是HTTP请求头的值,第三个参数我们暂时忽略它,总设置其为false。</p>

<p>在我们的示例代码中,我们为HTTP reques header中增加了被我们命名为X-Hello的HTTP请求头,其值为World</p>

<div class="note">
<p><s><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.)</s></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="/zh-cn/Observer_Notifications" title="zh-cn/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="/zh-cn/XPCOM_Interface_Reference/nsIObserver" title="zh-cn/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="/zh-cn/JavaScript" title="zh-cn/JavaScript">JavaScript</a> to be just like <a href="/zh-cn/Java" title="zh-cn/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="/zh-cn/XPCOM_Interface_Reference/nsISupports" title="zh-cn/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="/zh-cn/XPCOM_Interface_Reference/nsIObserverService" title="zh-cn/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.</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="/zh-cn/How_to_Build_an_XPCOM_Component_in_Javascript" title="zh-cn/How_to_Build_an_XPCOM_Component_in_Javascript">XPCOM component</a> instead of an <a href="/zh-cn/XUL_Overlays" title="zh-cn/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="/zh-cn/XPCOM/XPCOM_changes_in_Gecko_2.0#JavaScript_components" title="Linkification: https://developer.mozilla.org/zh-cn/XPCOM/XPCOM_changes_in_Gecko_2.0#JavaScript_components">https://developer.mozilla.org/zh-cn/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>