test_bug1984469.js (3106B)
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 const { NodeHTTP2Server } = ChromeUtils.importESModule( 12 "resource://testing-common/NodeServer.sys.mjs" 13 ); 14 15 function makeChan(uri, loadingUrl) { 16 let principal = Services.scriptSecurityManager.createContentPrincipal( 17 Services.io.newURI(loadingUrl), 18 {} 19 ); 20 return NetUtil.newChannel({ 21 uri, 22 loadingPrincipal: principal, 23 securityFlags: Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL, 24 contentPolicyType: Ci.nsIContentPolicy.TYPE_OTHER, 25 }); 26 } 27 28 class AuthPrompt { 29 constructor() { 30 this.QueryInterface = ChromeUtils.generateQI(["nsIAuthPrompt2"]); 31 } 32 asyncPromptAuth(channel, callback, context, encryptionLevel, authInfo) { 33 executeSoon(function () { 34 authInfo.username = "guest"; 35 authInfo.password = "guest"; 36 callback.onAuthAvailable(context, authInfo); 37 }); 38 } 39 } 40 41 class AuthRequestor { 42 constructor(prompt) { 43 this.prompt = prompt; 44 this.QueryInterface = ChromeUtils.generateQI(["nsIInterfaceRequestor"]); 45 } 46 getInterface(iid) { 47 if (iid.equals(Ci.nsIAuthPrompt2)) { 48 return this.prompt(); 49 } 50 throw Components.Exception("", Cr.NS_ERROR_NO_INTERFACE); 51 } 52 } 53 54 /** 55 * Verify HTTP/2 auth retry behavior: the server issues two 401 challenges 56 * and only returns 200 OK on the third request. 57 * 58 * This test ensures the channel performs two auth retries and succeeds on the 59 * third attempt. 60 * 61 */ 62 add_task(async function test_http2_auth_retry_twice() { 63 Services.prefs.setIntPref("network.auth.subresource-http-auth-allow", 2); 64 65 let server = new NodeHTTP2Server(); 66 await server.start(); 67 registerCleanupFunction(async () => { 68 await server.stop(); 69 }); 70 71 await server.registerPathHandler("/test", (req, res) => { 72 const hasAuth = 73 typeof req.headers.authorization === "string" && 74 !!req.headers.authorization.length; 75 global.count ??= 0; 76 global.count++; 77 if (!hasAuth || global.count < 3) { 78 res.stream.respond({ 79 ":status": 401, 80 "content-type": "text/plain; charset=utf-8", 81 "www-authenticate": 'Basic realm="secret"', 82 }); 83 res.end("Unauthorized\n"); 84 return; 85 } 86 87 res.stream.respond({ 88 ":status": 200, 89 "content-type": "text/plain; charset=utf-8", 90 }); 91 res.end("OK\n"); 92 }); 93 94 let chan = makeChan( 95 `https://localhost:${server.port()}/test`, 96 `https://localhost:${server.port()}` 97 ); 98 chan.notificationCallbacks = new AuthRequestor(() => new AuthPrompt()); 99 100 let req = await new Promise(resolve => { 101 chan.asyncOpen(new ChannelListener(resolve, null, CL_ALLOW_UNKNOWN_CL)); 102 }); 103 equal(req.status, Cr.NS_OK); 104 equal(req.QueryInterface(Ci.nsIHttpChannel).responseStatus, 200); 105 equal(req.QueryInterface(Ci.nsIHttpChannel).protocolVersion, "h2"); 106 });