test_servers.js (11784B)
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/. */ 4 5 "use strict"; 6 7 /* import-globals-from head_cache.js */ 8 /* import-globals-from head_cookies.js */ 9 /* import-globals-from head_channels.js */ 10 11 // We don't normally allow localhost channels to be proxied, but this 12 // is easier than updating all the certs and/or domains. 13 Services.prefs.setBoolPref("network.proxy.allow_hijacking_localhost", true); 14 registerCleanupFunction(() => { 15 Services.prefs.clearUserPref("network.proxy.allow_hijacking_localhost"); 16 }); 17 18 const { HttpServer } = ChromeUtils.importESModule( 19 "resource://testing-common/httpd.sys.mjs" 20 ); 21 const { 22 NodeHTTPServer, 23 NodeHTTPSServer, 24 NodeHTTP2Server, 25 NodeHTTPProxyServer, 26 NodeHTTPSProxyServer, 27 NodeHTTP2ProxyServer, 28 with_node_servers, 29 } = ChromeUtils.importESModule("resource://testing-common/NodeServer.sys.mjs"); 30 31 function makeChan(uri) { 32 let chan = NetUtil.newChannel({ 33 uri, 34 loadUsingSystemPrincipal: true, 35 }).QueryInterface(Ci.nsIHttpChannel); 36 chan.loadFlags = Ci.nsIChannel.LOAD_INITIAL_DOCUMENT_URI; 37 return chan; 38 } 39 40 function channelOpenPromise(chan, flags) { 41 return new Promise(resolve => { 42 function finish(req, buffer) { 43 resolve([req, buffer]); 44 } 45 chan.asyncOpen(new ChannelListener(finish, null, flags)); 46 }); 47 } 48 49 function registerSimplePathHandler(server, path) { 50 return server.registerPathHandler(path, (req, resp) => { 51 resp.writeHead(200); 52 resp.end("done"); 53 }); 54 } 55 56 function regiisterServerNamePathHandler(server, path) { 57 return server.registerPathHandler(path, (req, resp) => { 58 resp.writeHead(200); 59 resp.end(global.server_name); 60 }); 61 } 62 63 add_task(async function test_dual_stack() { 64 let httpserv = new HttpServer(); 65 let content = "ok"; 66 httpserv.registerPathHandler("/", function handler(metadata, response) { 67 response.setHeader("Content-Length", `${content.length}`); 68 response.bodyOutputStream.write(content, content.length); 69 }); 70 httpserv.start_dualStack(-1); 71 72 let chan = makeChan(`http://127.0.0.1:${httpserv.identity.primaryPort}/`); 73 let [, response] = await channelOpenPromise(chan); 74 Assert.equal(response, content); 75 76 chan = makeChan(`http://[::1]:${httpserv.identity.primaryPort}/`); 77 [, response] = await channelOpenPromise(chan); 78 Assert.equal(response, content); 79 await new Promise(resolve => httpserv.stop(resolve)); 80 }); 81 82 add_task(async function test_http() { 83 let server = new NodeHTTPServer(); 84 await server.start(); 85 registerCleanupFunction(async () => { 86 await server.stop(); 87 }); 88 let chan = makeChan(`http://localhost:${server.port()}/test`); 89 let req = await new Promise(resolve => { 90 chan.asyncOpen(new ChannelListener(resolve, null, CL_ALLOW_UNKNOWN_CL)); 91 }); 92 equal(req.status, Cr.NS_OK); 93 equal(req.QueryInterface(Ci.nsIHttpChannel).responseStatus, 404); 94 await registerSimplePathHandler(server, "/test"); 95 chan = makeChan(`http://localhost:${server.port()}/test`); 96 req = await new Promise(resolve => { 97 chan.asyncOpen(new ChannelListener(resolve, null, CL_ALLOW_UNKNOWN_CL)); 98 }); 99 equal(req.status, Cr.NS_OK); 100 equal(req.QueryInterface(Ci.nsIHttpChannel).responseStatus, 200); 101 equal(req.QueryInterface(Ci.nsIHttpChannel).protocolVersion, "http/1.1"); 102 equal(req.QueryInterface(Ci.nsIHttpChannelInternal).isProxyUsed, false); 103 104 await server.stop(); 105 }); 106 107 add_task(async function test_https() { 108 let server = new NodeHTTPSServer(); 109 await server.start(); 110 registerCleanupFunction(async () => { 111 await server.stop(); 112 }); 113 let chan = makeChan(`https://localhost:${server.port()}/test`); 114 let req = await new Promise(resolve => { 115 chan.asyncOpen(new ChannelListener(resolve, null, CL_ALLOW_UNKNOWN_CL)); 116 }); 117 equal(req.status, Cr.NS_OK); 118 equal(req.QueryInterface(Ci.nsIHttpChannel).responseStatus, 404); 119 await registerSimplePathHandler(server, "/test"); 120 chan = makeChan(`https://localhost:${server.port()}/test`); 121 req = await new Promise(resolve => { 122 chan.asyncOpen(new ChannelListener(resolve, null, CL_ALLOW_UNKNOWN_CL)); 123 }); 124 equal(req.status, Cr.NS_OK); 125 equal(req.QueryInterface(Ci.nsIHttpChannel).responseStatus, 200); 126 equal(req.QueryInterface(Ci.nsIHttpChannel).protocolVersion, "http/1.1"); 127 128 await server.stop(); 129 }); 130 131 add_task(async function test_http2() { 132 let server = new NodeHTTP2Server(); 133 await server.start(); 134 registerCleanupFunction(async () => { 135 await server.stop(); 136 }); 137 let chan = makeChan(`https://localhost:${server.port()}/test`); 138 let req = await new Promise(resolve => { 139 chan.asyncOpen(new ChannelListener(resolve, null, CL_ALLOW_UNKNOWN_CL)); 140 }); 141 equal(req.status, Cr.NS_OK); 142 equal(req.QueryInterface(Ci.nsIHttpChannel).responseStatus, 404); 143 await registerSimplePathHandler(server, "/test"); 144 chan = makeChan(`https://localhost:${server.port()}/test`); 145 req = await new Promise(resolve => { 146 chan.asyncOpen(new ChannelListener(resolve, null, CL_ALLOW_UNKNOWN_CL)); 147 }); 148 equal(req.status, Cr.NS_OK); 149 equal(req.QueryInterface(Ci.nsIHttpChannel).responseStatus, 200); 150 equal(req.QueryInterface(Ci.nsIHttpChannel).protocolVersion, "h2"); 151 152 await server.stop(); 153 }); 154 155 add_task(async function test_http1_proxy() { 156 let proxy = new NodeHTTPProxyServer(); 157 await proxy.start(); 158 registerCleanupFunction(async () => { 159 await proxy.stop(); 160 }); 161 162 let chan = makeChan(`http://localhost:${proxy.port()}/test`); 163 let req = await new Promise(resolve => { 164 chan.asyncOpen(new ChannelListener(resolve, null, CL_ALLOW_UNKNOWN_CL)); 165 }); 166 equal(req.status, Cr.NS_OK); 167 equal(req.QueryInterface(Ci.nsIHttpChannel).responseStatus, 405); 168 169 await with_node_servers( 170 [NodeHTTPServer, NodeHTTPSServer, NodeHTTP2Server], 171 async server => { 172 await server.execute( 173 `global.server_name = "${server.constructor.name}";` 174 ); 175 await regiisterServerNamePathHandler(server, "/test"); 176 let [req1, buff] = await channelOpenPromise( 177 makeChan(`${server.origin()}/test`), 178 CL_ALLOW_UNKNOWN_CL 179 ); 180 equal(req1.status, Cr.NS_OK); 181 equal(req1.QueryInterface(Ci.nsIHttpChannel).responseStatus, 200); 182 equal(buff, server.constructor.name); 183 //Bug 1792187: Check if proxy is set to true when a proxy is used. 184 equal(req1.QueryInterface(Ci.nsIHttpChannelInternal).isProxyUsed, true); 185 equal( 186 req1.QueryInterface(Ci.nsIHttpChannel).protocolVersion, 187 server.constructor.name == "NodeHTTP2Server" ? "h2" : "http/1.1" 188 ); 189 } 190 ); 191 192 await proxy.stop(); 193 }); 194 195 add_task(async function test_https_proxy() { 196 let proxy = new NodeHTTPSProxyServer(); 197 await proxy.start(); 198 registerCleanupFunction(async () => { 199 await proxy.stop(); 200 }); 201 202 let chan = makeChan(`https://localhost:${proxy.port()}/test`); 203 let req = await new Promise(resolve => { 204 chan.asyncOpen(new ChannelListener(resolve, null, CL_ALLOW_UNKNOWN_CL)); 205 }); 206 equal(req.status, Cr.NS_OK); 207 equal(req.QueryInterface(Ci.nsIHttpChannel).responseStatus, 405); 208 209 await with_node_servers( 210 [NodeHTTPServer, NodeHTTPSServer, NodeHTTP2Server], 211 async server => { 212 await server.execute( 213 `global.server_name = "${server.constructor.name}";` 214 ); 215 await regiisterServerNamePathHandler(server, "/test"); 216 217 let [req1, buff] = await channelOpenPromise( 218 makeChan(`${server.origin()}/test`), 219 CL_ALLOW_UNKNOWN_CL 220 ); 221 equal(req1.status, Cr.NS_OK); 222 equal(req1.QueryInterface(Ci.nsIHttpChannel).responseStatus, 200); 223 equal(buff, server.constructor.name); 224 } 225 ); 226 227 await proxy.stop(); 228 }); 229 230 add_task(async function test_http2_proxy() { 231 let proxy = new NodeHTTP2ProxyServer(); 232 await proxy.start(); 233 registerCleanupFunction(async () => { 234 await proxy.stop(); 235 }); 236 237 let chan = makeChan(`https://localhost:${proxy.port()}/test`); 238 let req = await new Promise(resolve => { 239 chan.asyncOpen(new ChannelListener(resolve, null, CL_ALLOW_UNKNOWN_CL)); 240 }); 241 equal(req.status, Cr.NS_OK); 242 equal(req.QueryInterface(Ci.nsIHttpChannel).responseStatus, 405); 243 244 await with_node_servers( 245 [NodeHTTPServer, NodeHTTPSServer, NodeHTTP2Server], 246 async server => { 247 await server.execute( 248 `global.server_name = "${server.constructor.name}";` 249 ); 250 await regiisterServerNamePathHandler(server, "/test"); 251 let [req1, buff] = await channelOpenPromise( 252 makeChan(`${server.origin()}/test`), 253 CL_ALLOW_UNKNOWN_CL 254 ); 255 equal(req1.status, Cr.NS_OK); 256 equal(req1.QueryInterface(Ci.nsIHttpChannel).responseStatus, 200); 257 equal(buff, server.constructor.name); 258 } 259 ); 260 261 await proxy.stop(); 262 }); 263 264 add_task(async function test_proxy_with_redirects() { 265 let proxies = [ 266 NodeHTTPProxyServer, 267 NodeHTTPSProxyServer, 268 NodeHTTP2ProxyServer, 269 ]; 270 for (let p of proxies) { 271 let proxy = new p(); 272 await proxy.start(); 273 registerCleanupFunction(async () => { 274 await proxy.stop(); 275 }); 276 277 await with_node_servers( 278 [NodeHTTPServer, NodeHTTPSServer, NodeHTTP2Server], 279 async server => { 280 info(`Testing ${p.name} with ${server.constructor.name}`); 281 await server.execute( 282 `global.server_name = "${server.constructor.name}";` 283 ); 284 await server.registerPathHandler("/redirect", (req, resp) => { 285 resp.writeHead(302, { 286 Location: "/test", 287 }); 288 resp.end(global.server_name); 289 }); 290 await server.registerPathHandler("/test", (req, resp) => { 291 resp.writeHead(200); 292 resp.end(global.server_name); 293 }); 294 295 let chan = makeChan(`${server.origin()}/redirect`); 296 let [req, buff] = await channelOpenPromise(chan, CL_ALLOW_UNKNOWN_CL); 297 equal(req.status, Cr.NS_OK); 298 equal(req.QueryInterface(Ci.nsIHttpChannel).responseStatus, 200); 299 equal(buff, server.constructor.name); 300 req.QueryInterface(Ci.nsIProxiedChannel); 301 ok(!!req.proxyInfo); 302 notEqual(req.proxyInfo.type, "direct"); 303 } 304 ); 305 await proxy.stop(); 306 } 307 }); 308 309 add_task(async function test_async_event() { 310 let server = new NodeHTTP2Server(); 311 await server.start(); 312 registerCleanupFunction(async () => { 313 await server.stop(); 314 }); 315 316 await server.execute(`new Promise(r => setTimeout(r, 500))`); 317 318 await server.stop(); 319 }); 320 321 add_task(async function test_async_state_management() { 322 let server = new NodeHTTP2Server(); 323 await server.start(); 324 registerCleanupFunction(async () => { 325 await server.stop(); 326 }); 327 328 await server.execute(`global.asyncResults = [];`); 329 330 await server.execute(` 331 global.asyncCounter = 0; 332 global.performAsyncOperation = function(delay, value) { 333 return new Promise(resolve => { 334 setTimeout(() => { 335 global.asyncCounter++; 336 global.asyncResults.push({ counter: global.asyncCounter, value }); 337 resolve({ counter: global.asyncCounter, value }); 338 }, delay); 339 }); 340 }; 341 `); 342 343 let op1 = server.execute(`performAsyncOperation(100, "first")`); 344 let op2 = server.execute(`performAsyncOperation(50, "second")`); 345 346 let result1 = await op1; 347 let result2 = await op2; 348 // This ran after 100 ms, so it comes in second 349 equal(result1.counter, 2); 350 equal(result1.value, "first"); 351 352 // this rand after 50 ms, so it comes in first. 353 equal(result2.counter, 1); 354 equal(result2.value, "second"); 355 356 let results = await server.execute(`global.asyncResults`); 357 equal(results.length, 2); 358 equal(results[0].value, "second"); 359 equal(results[1].value, "first"); 360 361 let counter = await server.execute(`global.asyncCounter`); 362 equal(counter, 2); 363 364 await server.stop(); 365 });