1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 /* globals setTimeout */
9 const { TestUtils
} = ChromeUtils
.importESModule(
10 "resource://testing-common/TestUtils.sys.mjs"
13 function makeChan(uri
) {
14 let chan
= NetUtil
.newChannel({
16 loadUsingSystemPrincipal
: true,
17 }).QueryInterface(Ci
.nsIHttpChannel
);
18 chan
.loadFlags
= Ci
.nsIChannel
.LOAD_INITIAL_DOCUMENT_URI
;
22 add_setup(async
function setup() {
24 Services
.prefs
.setIntPref("network.http.speculative-parallel-limit", 0);
25 registerCleanupFunction(async () => {
26 Services
.prefs
.clearUserPref("network.http.speculative-parallel-limit");
30 add_task(async
function test_cancel_after_asyncOpen() {
31 let certdb
= Cc
["@mozilla.org/security/x509certdb;1"].getService(
34 addCertFromFile(certdb
, "http2-ca.pem", "CTu,u,u");
35 addCertFromFile(certdb
, "proxy-ca.pem", "CTu,u,u");
42 for (let p
of proxies
) {
45 registerCleanupFunction(async () => {
48 await
with_node_servers(
49 [NodeHTTPServer
, NodeHTTPSServer
, NodeHTTP2Server
],
51 info(`Testing ${p.name} with ${server.constructor.name}`);
53 `global.server_name = "${server.constructor.name}";`
56 await server
.registerPathHandler("/test", (req
, resp
) => {
58 resp
.end(global
.server_name
);
61 let chan
= makeChan(`${server.origin()}/test`);
62 let openPromise
= new Promise(resolve
=> {
65 (req
, buff
) => resolve({ req
, buff
}),
71 chan
.cancel(Cr
.NS_ERROR_ABORT
);
72 let { req
} = await openPromise
;
73 Assert
.equal(req
.status
, Cr
.NS_ERROR_ABORT
);
80 // const NS_NET_STATUS_CONNECTING_TO = 0x4b0007;
81 // const NS_NET_STATUS_CONNECTED_TO = 0x4b0004;
82 // const NS_NET_STATUS_SENDING_TO = 0x4b0005;
83 const NS_NET_STATUS_WAITING_FOR
= 0x4b000a; // 2152398858
84 const NS_NET_STATUS_RECEIVING_FROM
= 0x4b0006;
85 // const NS_NET_STATUS_TLS_HANDSHAKE_STARTING = 0x4b000c; // 2152398860
86 // const NS_NET_STATUS_TLS_HANDSHAKE_ENDED = 0x4b000d; // 2152398861
88 add_task(async
function test_cancel_after_connect_http2proxy() {
89 let certdb
= Cc
["@mozilla.org/security/x509certdb;1"].getService(
92 addCertFromFile(certdb
, "http2-ca.pem", "CTu,u,u");
93 addCertFromFile(certdb
, "proxy-ca.pem", "CTu,u,u");
95 await
with_node_servers(
96 [NodeHTTPServer
, NodeHTTPSServer
, NodeHTTP2Server
],
98 // Set up a proxy for each server to make sure proxy state is clean
100 let proxy
= new NodeHTTP2ProxyServer();
102 registerCleanupFunction(async () => {
105 await proxy
.execute(`
106 global.session_counter = 0;
107 global.proxy.on("session", () => {
108 global.session_counter++;
112 info(`Testing ${proxy.constructor.name} with ${server.constructor.name}`);
113 await server
.execute(
114 `global.server_name = "${server.constructor.name}";`
117 await server
.registerPathHandler("/test", (req
, resp
) => {
118 global
.reqCount
= (global
.reqCount
|| 0) + 1;
120 resp
.end(global
.server_name
);
123 let chan
= makeChan(`${server.origin()}/test`);
124 chan
.notificationCallbacks
= {
125 QueryInterface
: ChromeUtils
.generateQI([
126 "nsIInterfaceRequestor",
127 "nsIProgressEventSink",
131 return this.QueryInterface(iid
);
135 onStatus(request
, status
) {
136 info(`status = ${status}`);
137 // XXX(valentin): Is this the best status to be cancelling?
138 if (status
== NS_NET_STATUS_WAITING_FOR
) {
139 info("cancelling connected channel");
140 chan
.cancel(Cr
.NS_ERROR_ABORT
);
144 let openPromise
= new Promise(resolve
=> {
147 (req
, buff
) => resolve({ req
, buff
}),
153 let { req
} = await openPromise
;
154 Assert
.equal(req
.status
, Cr
.NS_ERROR_ABORT
);
156 // Since we're cancelling just after connect, we'd expect that no
157 // requests are actually registered. But because we're cancelling on the
158 // main thread, and the request is being performed on the socket thread,
159 // it might actually reach the server, especially in chaos test mode.
161 // await server.execute(`global.reqCount || 0`),
163 // `No requests should have been made at this point`
165 Assert
.equal(await proxy
.execute(`global.session_counter`), 1);
167 chan
= makeChan(`${server.origin()}/test`);
168 await
new Promise(resolve
=> {
171 // eslint-disable-next-line no-shadow
172 (req
, buff
) => resolve({ req
, buff
}),
179 // Check that there's still only one session.
180 Assert
.equal(await proxy
.execute(`global.session_counter`), 1);
186 add_task(async
function test_cancel_after_sending_request() {
187 let certdb
= Cc
["@mozilla.org/security/x509certdb;1"].getService(
190 addCertFromFile(certdb
, "http2-ca.pem", "CTu,u,u");
191 addCertFromFile(certdb
, "proxy-ca.pem", "CTu,u,u");
193 await
with_node_servers(
194 [NodeHTTPServer
, NodeHTTPSServer
, NodeHTTP2Server
],
198 NodeHTTPSProxyServer
,
199 NodeHTTP2ProxyServer
,
201 for (let p
of proxies
) {
204 registerCleanupFunction(async () => {
208 await proxy
.execute(`
209 global.session_counter = 0;
210 global.proxy.on("session", () => {
211 global.session_counter++;
214 info(`Testing ${p.name} with ${server.constructor.name}`);
215 await server
.execute(
216 `global.server_name = "${server.constructor.name}";`
219 await server
.registerPathHandler("/test", (req
, resp
) => {
220 // Here we simmulate a slow response to give the test time to
221 // cancel the channel before receiving the response.
222 global
.request_count
= (global
.request_count
|| 0) + 1;
223 // eslint-disable-next-line mozilla/no-arbitrary-setTimeout
226 resp
.end(global
.server_name
);
229 await server
.registerPathHandler("/instant", (req
, resp
) => {
231 resp
.end(global
.server_name
);
234 // It seems proxy.on("session") only gets emitted after a full request.
235 // So we first load a simple request, then the long lasting request
236 // that we then cancel before it has the chance to complete.
237 let chan
= makeChan(`${server.origin()}/instant`);
238 await
new Promise(resolve
=> {
240 new ChannelListener(resolve
, null, CL_ALLOW_UNKNOWN_CL
)
244 chan
= makeChan(`${server.origin()}/test`);
245 let openPromise
= new Promise(resolve
=> {
248 (req
, buff
) => resolve({ req
, buff
}),
254 // XXX(valentin) This might be a little racy
255 await TestUtils
.waitForCondition(async () => {
256 return (await server
.execute("global.request_count")) > 0;
259 chan
.cancel(Cr
.NS_ERROR_ABORT
);
261 let { req
} = await openPromise
;
262 Assert
.equal(req
.status
, Cr
.NS_ERROR_ABORT
);
264 async
function checkSessionCounter() {
265 if (p
.name
== "NodeHTTP2ProxyServer") {
266 Assert
.equal(await proxy
.execute(`global.session_counter`), 1);
270 await
checkSessionCounter();
272 chan
= makeChan(`${server.origin()}/instant`);
273 await
new Promise(resolve
=> {
276 // eslint-disable-next-line no-shadow
277 (req
, buff
) => resolve({ req
, buff
}),
283 await
checkSessionCounter();
291 add_task(async
function test_cancel_during_response() {
292 let certdb
= Cc
["@mozilla.org/security/x509certdb;1"].getService(
295 addCertFromFile(certdb
, "http2-ca.pem", "CTu,u,u");
296 addCertFromFile(certdb
, "proxy-ca.pem", "CTu,u,u");
298 await
with_node_servers(
299 [NodeHTTPServer
, NodeHTTPSServer
, NodeHTTP2Server
],
303 NodeHTTPSProxyServer
,
304 NodeHTTP2ProxyServer
,
306 for (let p
of proxies
) {
309 registerCleanupFunction(async () => {
313 await proxy
.execute(`
314 global.session_counter = 0;
315 global.proxy.on("session", () => {
316 global.session_counter++;
320 info(`Testing ${p.name} with ${server.constructor.name}`);
321 await server
.execute(
322 `global.server_name = "${server.constructor.name}";`
325 await server
.registerPathHandler("/test", (req
, resp
) => {
327 resp
.write("a".repeat(1000));
328 // Here we send the response back in two chunks.
329 // The channel should be cancelled after the first one.
330 // eslint-disable-next-line mozilla/no-arbitrary-setTimeout
332 resp
.write("a".repeat(1000));
333 resp
.end(global
.server_name
);
336 await server
.registerPathHandler("/instant", (req
, resp
) => {
338 resp
.end(global
.server_name
);
341 let chan
= makeChan(`${server.origin()}/test`);
343 chan
.notificationCallbacks
= {
344 QueryInterface
: ChromeUtils
.generateQI([
345 "nsIInterfaceRequestor",
346 "nsIProgressEventSink",
350 return this.QueryInterface(iid
);
353 onProgress(request
, progress
, progressMax
) {
354 info(`progress: ${progress}/${progressMax}`);
355 // Check that we never get more than 1000 bytes.
356 Assert
.equal(progress
, 1000);
358 onStatus(request
, status
) {
359 if (status
== NS_NET_STATUS_RECEIVING_FROM
) {
360 info("cancelling when receiving request");
361 chan
.cancel(Cr
.NS_ERROR_ABORT
);
366 let openPromise
= new Promise(resolve
=> {
369 (req
, buff
) => resolve({ req
, buff
}),
376 let { req
} = await openPromise
;
377 Assert
.equal(req
.status
, Cr
.NS_ERROR_ABORT
);
379 async
function checkSessionCounter() {
380 if (p
.name
== "NodeHTTP2ProxyServer") {
381 Assert
.equal(await proxy
.execute(`global.session_counter`), 1);
385 await
checkSessionCounter();
387 chan
= makeChan(`${server.origin()}/instant`);
388 await
new Promise(resolve
=> {
391 // eslint-disable-next-line no-shadow
392 (req
, buff
) => resolve({ req
, buff
}),
399 await
checkSessionCounter();