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
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
|
<div id="pageData-name" class="pageData">Web Requests</div>
<!-- BEGIN AUTHORED CONTENT -->
<p id="classSummary">
Use the <code>chrome.webRequest</code> module to intercept, block,
or modify requests in-flight and to observe and analyze traffic.
</p>
<h2 id="manifest">Manifest</h2>
<p>You must declare the "webRequest" permission in the <a
href="manifest.html">extension manifest</a> to use the web request
API, along with <a href="manifest.html#permissions">host permissions</a>
for any hosts whose network requests you want to access. If you want to
use the web request API in a blocking fashion, you need to request
the "webRequestBlocking" permission in addition.
For example:</p>
<pre>{
"name": "My extension",
...
<b>"permissions": [
"webRequest",
"*://*.google.com"
]</b>,
...
}</pre>
<p class="note">
<b>Node:</b> If you request the "webRequestBlocking" permission, web requests
are delayed until the background page of your extension has been loaded. This
allows you to register event listeners before any web requests are processed.
In order to avoid deadlocks, you must not start synchronous XmlHttpRequests or
include scripts from the internet via <code><script src="..."></code> tags
in your background page.
</p>
<h2 id="life_cycle">Life cycle of requests</h2>
<p>
The web request API defines a set of events that follow the life cycle of a web
request. You can use these events to observe and analyze traffic. Certain
synchronous events will allow you to intercept, block, or modify a request.
</p>
<p>
The event life cycle for successful requests is illustrated here, followed by
event definitions:<br/>
<img src="images/webrequestapi.png"
width="385" height="503"
alt="Life cycle of a web request from the perspective of the webrequest API"
style="margin-left: auto; margin-right: auto; display: block"/>
</p>
<p>
<dl>
<dt><code>onBeforeRequest</code> (optionally synchronous)</dt>
<dd>Fires when a request is about to occur. This event is sent before any TCP
connection is made and can be used to cancel or redirect requests.</dd>
<dt><code>onBeforeSendHeaders</code> (optionally synchronous)</dt>
<dd>Fires when a request is about to occur and the initial headers have been
prepared. The event is intended to allow extensions to add, modify, and delete
request headers <a href="#life_cycle_footnote">(*)</a>. The
<code>onBeforeSendHeaders</code> event is passed to all subscribers, so
different subscribers may attempt to modify the request; see the <a
href="#implementation">Implementation details</a> section for how this is
handled. This event can be used to cancel the request.</dd>
<dt><code>onSendHeaders</code></dt>
<dd>Fires after all extensions have had a chance to modify the request
headers, and presents the final <a href="#life_cycle_footnote">(*)</a>
version. The event is triggered before the headers are sent to the network.
This event is informational and handled asynchronously. It does not allow
modifying or cancelling the request.</dd>
<dt><code>onHeadersReceived</code> (optionally synchronous)</dt>
<dd>Fires each time that an HTTP(S) response header is received. Due
to redirects and authentication requests this can happen multiple times per
request. This event is intended to allow extensions to add, modify, and delete
response headers, such as incoming Set-Cookie headers.</dd>
<dt><code>onAuthRequired</code> (optionally synchronous)</dt>
<dd>Fires when a request requires authentication of the user. This event can
be handled synchronously to provide authentication credentials. Note that
extensions may provide invalid credentials. Take care not to enter an infinite
loop by repeatedly providing invalid credentials.</dd>
<dt><code>onBeforeRedirect</code></dt>
<dd>Fires when a redirect is about to be executed. A redirection can be
triggered by an HTTP response code or by an extension. This event is
informational and handled asynchronously. It does not allow you to modify or
cancel the request. </dd>
<dt><code>onResponseStarted</code></dt>
<dd>Fires when the first byte of the response body is received. For HTTP
requests, this means that the status line and response headers are
available. This event is informational and handled asynchronously. It does not
allow modifying or cancelling the request.</dd>
<dt><code>onCompleted</code></dt>
<dd>Fires when a request has been processed successfully.</dd>
<dt><code>onErrorOccurred</code></dt>
<dd>Fires when a request could not be processed successfully.</dd>
</dl>
The web request API guarantees that for each request either
<code>onCompleted</code> or <code>onErrorOccurred</code> is fired as the final
event.
</p>
<p id="life_cycle_footnote">(*) Note that the web request API presents an
abstraction of the network stack to the extension. Internally, one URL request
can be split into several HTTP requests (for example to fetch individual byte
ranges from a large file) or can be handled by the network stack without
communicating with the network. For this reason, the API does not provide the
final HTTP headers that are sent to the network. For example, all headers that
are related to caching are invisible to the extension.</p>
<p>The following headers are currently <b>not provided</b> to the
<code>onBeforeSendHeaders</code> event. This list is not guaranteed to be
complete nor stable.
<ul>
<li>Authorization</li>
<li>Cache-Control</li>
<li>Connection</li>
<li>Content-Length</li>
<li>Host</li>
<li>If-Modified-Since</li>
<li>If-None-Match</li>
<li>If-Range</li>
<li>Partial-Data</li>
<li>Pragma</li>
<li>Proxy-Authorization</li>
<li>Proxy-Connection</li>
<li>Transfer-Encoding</li>
</ul>
</p>
<h2 id="concepts">Concepts</h2>
<p>As the following sections explain, events in the web request API use request
IDs, and you can optionally specify filters and extra information when you
register event listeners.</p>
<h3 id="Request IDs">Request IDs</h3>
<p>Each request is identified by a request ID. This ID is unique within a
browser session and the context of an extension. It remains constant during the
the life cycle of a request and can be used to match events for the same
request. Note that several HTTP requests are mapped to one web request in case
of HTTP redirection or HTTP authentication.</p>
<h3 id="subscription">Registering event listeners</h3>
<p>To register an event listener for a web request, you use a variation on the
<a href="events.html">usual <code>addListener()</code> function</a>.
In addition to specifying a callback function,
you have to specify a filter argument and you may specify an optional extra info
argument.</p>
<p>The three arguments to the web request API's <code>addListener()</code> have
the following definitions:</p>
<pre>
var callback = function(details) {...};
var filter = {...};
var opt_extraInfoSpec = [...];
</pre>
<p>Here's an example of listening for the <code>onBeforeRequest</code>
event:</p>
<pre>
chrome.webRequest.onBeforeRequest.addListener(
callback, filter, opt_extraInfoSpec);
</pre>
<p>Each <code>addListener()</code> call takes a mandatory callback function as
the first parameter. This callback function is passed a dictionary containing
information about the current URL request. The information in this dictionary
depends on the specific event type as well as the content of
<code>opt_extraInfoSpec</code>.</p>
<p>If the optional <code>opt_extraInfoSpec</code> array contains the string
<code>'blocking'</code> (only allowed for specific events), the callback
function is handled synchronously. That means that the request is blocked until
the callback function returns. In this case, the callback can return a <a
href="#type-BlockingResponse">BlockingResponse</a> that determines the further
life cycle of the request. Depending on the context, this response allows
cancelling or redirecting a request (<code>onBeforeRequest</code>), cancelling a
request or modifying headers (<code>onBeforeSendHeaders</code>,
<code>onHeadersReceived</code>), or providing authentication credentials
(<code>onAuthRequired</code>).</p>
<p>The <a href="#type-RequestFilter">RequestFilter</a>
<code>filter</code> allows limiting the requests for which events are
triggered in various dimensions:
<dl>
<dt>URLs</dt>
<dd><a href="match_patterns.html">URL patterns</a> such as
<code>*://www.google.com/foo*bar</code>.</dd>
<dt>Types</dt>
<dd>Request types such as <code>main_frame</code> (a document that is loaded
for a top-level frame), <code>sub_frame</code> (a document that is loaded for
an embedded frame), and <code>image</code> (an image on a web site).
See <a href="#type-RequestFilter">RequestFilter</a>.</dd>
<dt>Tab ID</dt>
<dd>The identifier for one tab.</dd>
<dt>Window ID</dt>
<dd>The identifier for a window.</dd>
</p>
<p>Depending on the event type, you can specify strings in
<code>opt_extraInfoSpec</code> to ask for additional information about the
request. This is used to provide detailed information on request's data only
if explicitly requested.</p>
<h2 id="implementation">Implementation details</h2>
<p>Several implementation details can be important to understand when developing
an extension that uses the web request API:</p>
<h3>Conflict resolution</h3>
<p>In the current implementation of the web request API, a request is considered
as cancelled if at least one extension instructs to cancel the request. If
an extension cancels a request, all extensions are notified by an
<code>onErrorOccurred</code> event. Only one extension is allowed to redirect a
request or modify a header at a time. If more than one extension attempts to
modify the request, the most recently installed extension wins and all others
are ignored. An extension is not notified if its instruction to modify or
redirect has been ignored.</p>
<h3>Caching</h3>
<p>
Chrome employs two caches — an on-disk cache and a very fast in-memory
cache. The lifetime of an in-memory cache is attached to the lifetime of a
render process, which roughly corresponds to a tab. Requests that are answered
from the in-memory cache are invisible to the web request API. If a request
handler changes its behavior (for example, the behavior according to which
requests are blocked), a simple page refresh might not respect this changed
behavior. To make sure the behavior change goes through, call
<code>handlerBehaviorChanged()</code> to flush the in-memory cache. But don't do
it often; flushing the cache is a very expensive operation. You don't need to
call <code>handlerBehaviorChanged()</code> after registering or unregistering an
event listener.</p>
<h3>Timestamps</h3>
<p>
The <code>timestamp</code> property of web request events is only guaranteed to
be <i>internally</i> consistent. Comparing one event to another event will give
you the correct offset between them, but comparing them to the current time
inside the extension (via <code>(new Date()).getTime()</code>, for instance)
might give unexpected results.
</p>
<h2 id="examples">Examples</h2>
<p>The following example illustrates how to block all requests to
<code>www.evil.com</code>:</p>
<pre>
chrome.webRequest.onBeforeRequest.addListener(
function(details) {
return {cancel: details.url.indexOf("://www.evil.com/") != -1};
},
{urls: ["<all_urls>"]},
["blocking"]);
</pre>
<p>As this function uses a blocking event handler, it requires the "webRequest"
as well as the "webRequestBlocking" permission in the manifest file.</p>
<p>The following example achieves the same goal in a more efficient way because
requests that are not targeted to <code>www.evil.com</code> do not need to be
passed to the extension:</p>
<pre>
chrome.webRequest.onBeforeRequest.addListener(
function(details) { return {cancel: true}; },
{urls: ["*://www.evil.com/*"]},
["blocking"]);
</pre>
<p>The following example illustrates how to delete the User-Agent header from
all requests:</p>
<pre>
chrome.webRequest.onBeforeSendHeaders.addListener(
function(details) {
for (var i = 0; i < details.requestHeaders.length; ++i) {
if (details.requestHeaders[i].name === 'User-Agent') {
details.requestHeaders.splice(i, 1);
break;
}
}
return {requestHeaders: details.requestHeaders};
},
{urls: ["<all_urls>"]},
["blocking", "requestHeaders"]);
</pre>
<p> For more example code, see the <a href="samples.html#webrequest">web request
samples</a>.</p>
<!-- END AUTHORED CONTENT -->
|