test_stale-while-revalidate_positive.js (4250B)
1 /* 2 3 Tests the Cache-control: stale-while-revalidate response directive. 4 5 Purpose is to check we perform the background revalidation when the window is set 6 and we hit it. 7 8 * Make request #1. 9 - response is from the server and version=1 10 - max-age=1, stale-while-revalidate=9999 11 * Switch version of the data on the server and prolong the max-age to not let req #3 12 do a bck reval at the end of the test (prevent leaks/shutdown races.) 13 * Make request #2 in 2 seconds (entry should be expired by that time, but fall into 14 the reval window.) 15 - response is from the cache, version=1 16 - a new background request should be made for the data 17 * Wait for "http-on-background-revalidation" notifying finish of the background reval. 18 * Make request #3. 19 - response is from the cache, version=2 20 * Done. 21 22 */ 23 24 "use strict"; 25 26 const { HttpServer } = ChromeUtils.importESModule( 27 "resource://testing-common/httpd.sys.mjs" 28 ); 29 30 let max_age; 31 let version; 32 let expectCookies = false; 33 let generate_response = ver => `response version=${ver}`; 34 35 function test_handler(metadata, response) { 36 // ensures initial network request and background revalidation request sends custom headers 37 // See Bug 1893842 38 if ( 39 !metadata.hasHeader("X-Custom-header") || 40 metadata.getHeader("X-Custom-header") != "custom-value" 41 ) { 42 response.setStatusLine(metadata.httpVersion, 400, "OK"); 43 return; 44 } 45 46 // Check if the request has a cookie 47 if (expectCookies) { 48 let cookies = metadata.getHeader("Cookie"); 49 if (cookies !== "Custom=CustomValue") { 50 response.setStatusLine(metadata.httpVersion, 400, "Cookies dont match"); 51 return; 52 } 53 } 54 55 // Set the cookie in the response 56 response.setHeader( 57 "Set-Cookie", 58 "Custom=CustomValue; HttpOnly; Path=/", 59 true 60 ); 61 62 const originalBody = generate_response(version); 63 response.setHeader("Content-Type", "text/html", false); 64 response.setHeader( 65 "Cache-control", 66 `max-age=${max_age}, stale-while-revalidate=9999`, 67 false 68 ); 69 70 response.setStatusLine(metadata.httpVersion, 200, "OK"); 71 response.bodyOutputStream.write(originalBody, originalBody.length); 72 } 73 74 function make_channel(url) { 75 var chan = NetUtil.newChannel({ 76 uri: url, 77 loadUsingSystemPrincipal: true, 78 }).QueryInterface(Ci.nsIHttpChannel); 79 chan.setRequestHeader("X-Custom-header", "custom-value", false); 80 return chan; 81 } 82 83 async function get_response(channel, fromCache) { 84 return new Promise(resolve => { 85 channel.asyncOpen( 86 new ChannelListener((request, buffer, ctx, isFromCache) => { 87 Assert.equal( 88 fromCache, 89 isFromCache, 90 `got response from cache = ${fromCache}` 91 ); 92 resolve(buffer); 93 }) 94 ); 95 }); 96 } 97 98 async function sleep(time) { 99 return new Promise(resolve => { 100 do_timeout(time * 1000, resolve); 101 }); 102 } 103 104 async function stop_server(httpserver) { 105 return new Promise(resolve => { 106 httpserver.stop(resolve); 107 }); 108 } 109 110 async function background_reval_promise() { 111 return new Promise(resolve => { 112 Services.obs.addObserver(resolve, "http-on-background-revalidation"); 113 }); 114 } 115 116 add_task(async function () { 117 Services.prefs.setIntPref("network.cookie.cookieBehavior", 0); 118 Services.prefs.setBoolPref( 119 "network.cookieJarSettings.unblocked_for_testing", 120 true 121 ); 122 let httpserver = new HttpServer(); 123 httpserver.registerPathHandler("/testdir", test_handler); 124 httpserver.start(-1); 125 const PORT = httpserver.identity.primaryPort; 126 const URI = `http://localhost:${PORT}/testdir`; 127 128 let response; 129 130 version = 1; 131 max_age = 1; 132 response = await get_response(make_channel(URI), false); 133 Assert.equal(response, generate_response(1), "got response ver 1"); 134 expectCookies = true; 135 await sleep(max_age + 1); 136 137 // must specifically wait for the internal channel to finish the reval to make 138 // the test race-free. 139 let reval_done = background_reval_promise(); 140 141 version = 2; 142 max_age = 100; 143 response = await get_response(make_channel(URI), true); 144 Assert.equal(response, generate_response(1), "got response ver 1"); 145 146 await reval_done; 147 148 response = await get_response(make_channel(URI), true); 149 Assert.equal(response, generate_response(2), "got response ver 2"); 150 151 await stop_server(httpserver); 152 });