test_XHR_redirects.js (8106B)
1 // This file tests whether XmlHttpRequests correctly handle redirects, 2 // including rewriting POSTs to GETs (on 301/302/303), as well as 3 // prompting for redirects of other unsafe methods (such as PUTs, DELETEs, 4 // etc--see HttpBaseChannel::IsSafeMethod). Since no prompting is possible 5 // in xpcshell, we get an error for prompts, and the request fails. 6 "use strict"; 7 8 const { HttpServer } = ChromeUtils.importESModule( 9 "resource://testing-common/httpd.sys.mjs" 10 ); 11 const { Preferences } = ChromeUtils.importESModule( 12 "resource://gre/modules/Preferences.sys.mjs" 13 ); 14 15 var sSame; 16 var sOther; 17 var sRedirectPromptPref; 18 19 const BUGID = "676059"; 20 const OTHERBUGID = "696849"; 21 22 ChromeUtils.defineLazyGetter(this, "pSame", function () { 23 return sSame.identity.primaryPort; 24 }); 25 ChromeUtils.defineLazyGetter(this, "pOther", function () { 26 return sOther.identity.primaryPort; 27 }); 28 29 function createXHR(async, method, path) { 30 var xhr = new XMLHttpRequest(); 31 xhr.open(method, "http://localhost:" + pSame + path, async); 32 return xhr; 33 } 34 35 function checkResults(xhr, method, status, unsafe) { 36 if (unsafe) { 37 if (sRedirectPromptPref) { 38 // The method is null if we prompt for unsafe redirects 39 method = null; 40 } else { 41 // The status code is 200 when we don't prompt for unsafe redirects 42 status = 200; 43 } 44 } 45 46 if (xhr.readyState != 4) { 47 return false; 48 } 49 Assert.equal(xhr.status, status); 50 51 if (status == 200) { 52 // if followed then check for echoed method name 53 Assert.equal(xhr.getResponseHeader("X-Received-Method"), method); 54 } 55 56 return true; 57 } 58 59 function run_test() { 60 // start servers 61 sSame = new HttpServer(); 62 63 // same-origin redirects 64 sSame.registerPathHandler( 65 "/bug" + BUGID + "-redirect301", 66 bug676059redirect301 67 ); 68 sSame.registerPathHandler( 69 "/bug" + BUGID + "-redirect302", 70 bug676059redirect302 71 ); 72 sSame.registerPathHandler( 73 "/bug" + BUGID + "-redirect303", 74 bug676059redirect303 75 ); 76 sSame.registerPathHandler( 77 "/bug" + BUGID + "-redirect307", 78 bug676059redirect307 79 ); 80 sSame.registerPathHandler( 81 "/bug" + BUGID + "-redirect308", 82 bug676059redirect308 83 ); 84 85 // cross-origin redirects 86 sSame.registerPathHandler( 87 "/bug" + OTHERBUGID + "-redirect301", 88 bug696849redirect301 89 ); 90 sSame.registerPathHandler( 91 "/bug" + OTHERBUGID + "-redirect302", 92 bug696849redirect302 93 ); 94 sSame.registerPathHandler( 95 "/bug" + OTHERBUGID + "-redirect303", 96 bug696849redirect303 97 ); 98 sSame.registerPathHandler( 99 "/bug" + OTHERBUGID + "-redirect307", 100 bug696849redirect307 101 ); 102 sSame.registerPathHandler( 103 "/bug" + OTHERBUGID + "-redirect308", 104 bug696849redirect308 105 ); 106 107 // same-origin target 108 sSame.registerPathHandler("/bug" + BUGID + "-target", echoMethod); 109 sSame.start(-1); 110 111 // cross-origin target 112 sOther = new HttpServer(); 113 sOther.registerPathHandler("/bug" + OTHERBUGID + "-target", echoMethod); 114 sOther.start(-1); 115 116 // format: redirectType, methodToSend, redirectedMethod, finalStatus 117 // redirectType sets the URI the initial request goes to 118 // methodToSend is the HTTP method to send 119 // redirectedMethod is the method to use for the redirect, if any 120 // finalStatus is 200 when the redirect takes place, redirectType otherwise 121 122 // Note that unsafe methods should not follow the redirect automatically 123 // Of the methods below, DELETE, POST and PUT are unsafe 124 125 sRedirectPromptPref = Preferences.get("network.http.prompt-temp-redirect"); 126 // Following Bug 677754 we don't prompt for unsafe redirects 127 128 // same-origin variant 129 var tests = [ 130 // 301: rewrite just POST 131 [301, "DELETE", "DELETE", 301, true], 132 [301, "GET", "GET", 200, false], 133 [301, "HEAD", "HEAD", 200, false], 134 [301, "POST", "GET", 200, false], 135 [301, "PUT", "PUT", 301, true], 136 [301, "PROPFIND", "PROPFIND", 200, false], 137 // 302: see 301 138 [302, "DELETE", "DELETE", 302, true], 139 [302, "GET", "GET", 200, false], 140 [302, "HEAD", "HEAD", 200, false], 141 [302, "POST", "GET", 200, false], 142 [302, "PUT", "PUT", 302, true], 143 [302, "PROPFIND", "PROPFIND", 200, false], 144 // 303: rewrite to GET except HEAD 145 [303, "DELETE", "GET", 200, false], 146 [303, "GET", "GET", 200, false], 147 [303, "HEAD", "HEAD", 200, false], 148 [303, "POST", "GET", 200, false], 149 [303, "PUT", "GET", 200, false], 150 [303, "PROPFIND", "GET", 200, false], 151 // 307: never rewrite 152 [307, "DELETE", "DELETE", 307, true], 153 [307, "GET", "GET", 200, false], 154 [307, "HEAD", "HEAD", 200, false], 155 [307, "POST", "POST", 307, true], 156 [307, "PUT", "PUT", 307, true], 157 [307, "PROPFIND", "PROPFIND", 200, false], 158 // 308: never rewrite 159 [308, "DELETE", "DELETE", 308, true], 160 [308, "GET", "GET", 200, false], 161 [308, "HEAD", "HEAD", 200, false], 162 [308, "POST", "POST", 308, true], 163 [308, "PUT", "PUT", 308, true], 164 [308, "PROPFIND", "PROPFIND", 200, false], 165 ]; 166 167 // cross-origin variant 168 var othertests = tests; // for now these have identical results 169 170 var xhr; 171 172 for (let i = 0; i < tests.length; ++i) { 173 dump("Testing " + tests[i] + "\n"); 174 xhr = createXHR( 175 false, 176 tests[i][1], 177 "/bug" + BUGID + "-redirect" + tests[i][0] 178 ); 179 xhr.send(null); 180 checkResults(xhr, tests[i][2], tests[i][3], tests[i][4]); 181 } 182 183 for (let i = 0; i < othertests.length; ++i) { 184 dump("Testing " + othertests[i] + " (cross-origin)\n"); 185 xhr = createXHR( 186 false, 187 othertests[i][1], 188 "/bug" + OTHERBUGID + "-redirect" + othertests[i][0] 189 ); 190 xhr.send(null); 191 checkResults(xhr, othertests[i][2], tests[i][3], tests[i][4]); 192 } 193 194 sSame.stop(do_test_finished); 195 sOther.stop(do_test_finished); 196 } 197 198 function redirect(metadata, response, status, port, bugid) { 199 // set a proper reason string to avoid confusion when looking at the 200 // HTTP messages 201 var reason; 202 if (status == 301) { 203 reason = "Moved Permanently"; 204 } else if (status == 302) { 205 reason = "Found"; 206 } else if (status == 303) { 207 reason = "See Other"; 208 } else if (status == 307) { 209 reason = "Temporary Redirect"; 210 } else if (status == 308) { 211 reason = "Permanent Redirect"; 212 } 213 214 response.setStatusLine(metadata.httpVersion, status, reason); 215 response.setHeader( 216 "Location", 217 "http://localhost:" + port + "/bug" + bugid + "-target" 218 ); 219 } 220 221 // PATH HANDLER FOR /bug676059-redirect301 222 function bug676059redirect301(metadata, response) { 223 redirect(metadata, response, 301, pSame, BUGID); 224 } 225 226 // PATH HANDLER FOR /bug696849-redirect301 227 function bug696849redirect301(metadata, response) { 228 redirect(metadata, response, 301, pOther, OTHERBUGID); 229 } 230 231 // PATH HANDLER FOR /bug676059-redirect302 232 function bug676059redirect302(metadata, response) { 233 redirect(metadata, response, 302, pSame, BUGID); 234 } 235 236 // PATH HANDLER FOR /bug696849-redirect302 237 function bug696849redirect302(metadata, response) { 238 redirect(metadata, response, 302, pOther, OTHERBUGID); 239 } 240 241 // PATH HANDLER FOR /bug676059-redirect303 242 function bug676059redirect303(metadata, response) { 243 redirect(metadata, response, 303, pSame, BUGID); 244 } 245 246 // PATH HANDLER FOR /bug696849-redirect303 247 function bug696849redirect303(metadata, response) { 248 redirect(metadata, response, 303, pOther, OTHERBUGID); 249 } 250 251 // PATH HANDLER FOR /bug676059-redirect307 252 function bug676059redirect307(metadata, response) { 253 redirect(metadata, response, 307, pSame, BUGID); 254 } 255 256 // PATH HANDLER FOR /bug676059-redirect308 257 function bug676059redirect308(metadata, response) { 258 redirect(metadata, response, 308, pSame, BUGID); 259 } 260 261 // PATH HANDLER FOR /bug696849-redirect307 262 function bug696849redirect307(metadata, response) { 263 redirect(metadata, response, 307, pOther, OTHERBUGID); 264 } 265 266 // PATH HANDLER FOR /bug696849-redirect308 267 function bug696849redirect308(metadata, response) { 268 redirect(metadata, response, 308, pOther, OTHERBUGID); 269 } 270 271 // Echo the request method in "X-Received-Method" header field 272 function echoMethod(metadata, response) { 273 response.setStatusLine(metadata.httpVersion, 200, "OK"); 274 response.setHeader("X-Received-Method", metadata.method); 275 }