test_suspend_channel_on_authRetry.js (6658B)
1 // This file tests async handling of a channel suspended in DoAuthRetry 2 // notifying http-on-modify-request and http-on-before-connect observers. 3 "use strict"; 4 5 const { HttpServer } = ChromeUtils.importESModule( 6 "resource://testing-common/httpd.sys.mjs" 7 ); 8 9 ChromeUtils.defineLazyGetter(this, "URL", function () { 10 return "http://localhost:" + httpserv.identity.primaryPort; 11 }); 12 13 var obs = Services.obs; 14 15 var requestObserver = null; 16 17 function AuthPrompt() {} 18 19 AuthPrompt.prototype = { 20 user: "guest", 21 pass: "guest", 22 23 QueryInterface: ChromeUtils.generateQI(["nsIAuthPrompt"]), 24 25 prompt: function ap1_prompt() { 26 do_throw("unexpected prompt call"); 27 }, 28 29 promptUsernameAndPassword: function promptUP( 30 title, 31 text, 32 realm, 33 savePW, 34 user, 35 pw 36 ) { 37 user.value = this.user; 38 pw.value = this.pass; 39 40 obs.addObserver(requestObserver, "http-on-before-connect"); 41 obs.addObserver(requestObserver, "http-on-modify-request"); 42 return true; 43 }, 44 45 promptPassword: function promptPW() { 46 do_throw("unexpected promptPassword call"); 47 }, 48 }; 49 50 function requestListenerObserver( 51 suspendOnBeforeConnect, 52 suspendOnModifyRequest 53 ) { 54 this.suspendOnModifyRequest = suspendOnModifyRequest; 55 this.suspendOnBeforeConnect = suspendOnBeforeConnect; 56 } 57 58 requestListenerObserver.prototype = { 59 suspendOnModifyRequest: false, 60 suspendOnBeforeConnect: false, 61 gotOnBeforeConnect: false, 62 resumeOnBeforeConnect: false, 63 gotOnModifyRequest: false, 64 resumeOnModifyRequest: false, 65 QueryInterface: ChromeUtils.generateQI(["nsIObserver"]), 66 67 observe(subject, topic) { 68 if ( 69 topic === "http-on-before-connect" && 70 subject instanceof Ci.nsIHttpChannel 71 ) { 72 if (this.suspendOnBeforeConnect) { 73 let chan = subject.QueryInterface(Ci.nsIHttpChannel); 74 executeSoon(() => { 75 this.resumeOnBeforeConnect = true; 76 chan.resume(); 77 }); 78 this.gotOnBeforeConnect = true; 79 chan.suspend(); 80 } 81 } else if ( 82 topic === "http-on-modify-request" && 83 subject instanceof Ci.nsIHttpChannel 84 ) { 85 if (this.suspendOnModifyRequest) { 86 let chan = subject.QueryInterface(Ci.nsIHttpChannel); 87 executeSoon(() => { 88 this.resumeOnModifyRequest = true; 89 chan.resume(); 90 }); 91 this.gotOnModifyRequest = true; 92 chan.suspend(); 93 } 94 } 95 }, 96 }; 97 98 function Requestor() {} 99 100 Requestor.prototype = { 101 QueryInterface: ChromeUtils.generateQI(["nsIInterfaceRequestor"]), 102 103 getInterface: function requestor_gi(iid) { 104 if (iid.equals(Ci.nsIAuthPrompt)) { 105 // Allow the prompt to store state by caching it here 106 if (!this.prompt) { 107 this.prompt = new AuthPrompt(); 108 } 109 return this.prompt; 110 } 111 112 throw Components.Exception("", Cr.NS_ERROR_NO_INTERFACE); 113 }, 114 115 prompt: null, 116 }; 117 118 var listener = { 119 expectedCode: -1, // Uninitialized 120 121 onStartRequest: function test_onStartR(request) { 122 try { 123 if (!Components.isSuccessCode(request.status)) { 124 do_throw("Channel should have a success code!"); 125 } 126 127 if (!(request instanceof Ci.nsIHttpChannel)) { 128 do_throw("Expecting an HTTP channel"); 129 } 130 131 Assert.equal(request.responseStatus, this.expectedCode); 132 // The request should be succeeded iff we expect 200 133 Assert.equal(request.requestSucceeded, this.expectedCode == 200); 134 } catch (e) { 135 do_throw("Unexpected exception: " + e); 136 } 137 throw Components.Exception("", Cr.NS_ERROR_ABORT); 138 }, 139 140 onDataAvailable: function test_ODA() { 141 do_throw("Should not get any data!"); 142 }, 143 144 onStopRequest: function test_onStopR(request, status) { 145 Assert.equal(status, Cr.NS_ERROR_ABORT); 146 if (requestObserver.suspendOnBeforeConnect) { 147 Assert.ok( 148 requestObserver.gotOnBeforeConnect && 149 requestObserver.resumeOnBeforeConnect 150 ); 151 } 152 if (requestObserver.suspendOnModifyRequest) { 153 Assert.ok( 154 requestObserver.gotOnModifyRequest && 155 requestObserver.resumeOnModifyRequest 156 ); 157 } 158 obs.removeObserver(requestObserver, "http-on-before-connect"); 159 obs.removeObserver(requestObserver, "http-on-modify-request"); 160 moveToNextTest(); 161 }, 162 }; 163 164 function makeChan(url, loadingUrl) { 165 var principal = Services.scriptSecurityManager.createContentPrincipal( 166 Services.io.newURI(loadingUrl), 167 {} 168 ); 169 return NetUtil.newChannel({ 170 uri: url, 171 loadingPrincipal: principal, 172 securityFlags: Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL, 173 contentPolicyType: Ci.nsIContentPolicy.TYPE_OTHER, 174 }); 175 } 176 177 var tests = [ 178 test_suspend_on_before_connect, 179 test_suspend_on_modify_request, 180 test_suspend_all, 181 ]; 182 183 var current_test = 0; 184 185 var httpserv = null; 186 187 function moveToNextTest() { 188 if (current_test < tests.length - 1) { 189 // First, gotta clear the auth cache 190 Cc["@mozilla.org/network/http-auth-manager;1"] 191 .getService(Ci.nsIHttpAuthManager) 192 .clearAll(); 193 194 current_test++; 195 tests[current_test](); 196 } else { 197 do_test_pending(); 198 httpserv.stop(do_test_finished); 199 } 200 201 do_test_finished(); 202 } 203 204 function run_test() { 205 httpserv = new HttpServer(); 206 207 httpserv.registerPathHandler("/auth", authHandler); 208 209 httpserv.start(-1); 210 211 tests[0](); 212 } 213 214 function test_suspend_on_auth(suspendOnBeforeConnect, suspendOnModifyRequest) { 215 var chan = makeChan(URL + "/auth", URL); 216 requestObserver = new requestListenerObserver( 217 suspendOnBeforeConnect, 218 suspendOnModifyRequest 219 ); 220 chan.notificationCallbacks = new Requestor(); 221 listener.expectedCode = 200; // OK 222 chan.asyncOpen(listener); 223 224 do_test_pending(); 225 } 226 227 function test_suspend_on_before_connect() { 228 test_suspend_on_auth(true, false); 229 } 230 231 function test_suspend_on_modify_request() { 232 test_suspend_on_auth(false, true); 233 } 234 235 function test_suspend_all() { 236 test_suspend_on_auth(true, true); 237 } 238 239 // PATH HANDLERS 240 241 // /auth 242 function authHandler(metadata, response) { 243 // btoa("guest:guest"), but that function is not available here 244 var expectedHeader = "Basic Z3Vlc3Q6Z3Vlc3Q="; 245 246 var body; 247 if ( 248 metadata.hasHeader("Authorization") && 249 metadata.getHeader("Authorization") == expectedHeader 250 ) { 251 response.setStatusLine(metadata.httpVersion, 200, "OK, authorized"); 252 response.setHeader("WWW-Authenticate", 'Basic realm="secret"', false); 253 254 body = "success"; 255 } else { 256 // didn't know guest:guest, failure 257 response.setStatusLine(metadata.httpVersion, 401, "Unauthorized"); 258 response.setHeader("WWW-Authenticate", 'Basic realm="secret"', false); 259 260 body = "failed"; 261 } 262 263 response.bodyOutputStream.write(body, body.length); 264 }