1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199
|
// Utility functions to help testing keepalive requests.
// Returns a URL to an iframe that loads a keepalive URL on iframe loaded.
//
// The keepalive URL points to a target that stores `token`. The token will then
// be posted back on iframe loaded to the parent document.
// `method` defaults to GET.
// `frameOrigin` to specify the origin of the iframe to load. If not set,
// default to a different site origin.
// `requestOrigin` to specify the origin of the fetch request target.
// `sendOn` to specify the name of the event when the keepalive request should
// be sent instead of the default 'load'.
// `mode` to specify the fetch request's CORS mode.
// `disallowCrossOrigin` to ask the iframe to set up a server that disallows
// cross origin requests.
function getKeepAliveIframeUrl(token, method, {
frameOrigin = 'DEFAULT',
requestOrigin = '',
sendOn = 'load',
mode = 'cors',
disallowCrossOrigin = false
} = {}) {
const https = location.protocol.startsWith('https');
frameOrigin = frameOrigin === 'DEFAULT' ?
get_host_info()[https ? 'HTTPS_NOTSAMESITE_ORIGIN' : 'HTTP_NOTSAMESITE_ORIGIN'] :
frameOrigin;
return `${frameOrigin}/fetch/api/resources/keepalive-iframe.html?` +
`token=${token}&` +
`method=${method}&` +
`sendOn=${sendOn}&` +
`mode=${mode}&` + (disallowCrossOrigin ? `disallowCrossOrigin=1&` : ``) +
`origin=${requestOrigin}`;
}
// Returns a different-site URL to an iframe that loads a keepalive URL.
//
// By default, the keepalive URL points to a target that redirects to another
// same-origin destination storing `token`. The token will then be posted back
// to parent document.
//
// The URL redirects can be customized from `origin1` to `origin2` if provided.
// Sets `withPreflight` to true to get URL enabling preflight.
function getKeepAliveAndRedirectIframeUrl(
token, origin1, origin2, withPreflight) {
const https = location.protocol.startsWith('https');
const frameOrigin =
get_host_info()[https ? 'HTTPS_NOTSAMESITE_ORIGIN' : 'HTTP_NOTSAMESITE_ORIGIN'];
return `${frameOrigin}/fetch/api/resources/keepalive-redirect-iframe.html?` +
`token=${token}&` +
`origin1=${origin1}&` +
`origin2=${origin2}&` + (withPreflight ? `with-headers` : ``);
}
async function iframeLoaded(iframe) {
return new Promise((resolve) => iframe.addEventListener('load', resolve));
}
// Obtains the token from the message posted by iframe after loading
// `getKeepAliveAndRedirectIframeUrl()`.
async function getTokenFromMessage() {
return new Promise((resolve) => {
window.addEventListener('message', (event) => {
resolve(event.data);
}, {once: true});
});
}
// Tells if `token` has been stored in the server.
async function queryToken(token) {
const response = await fetch(`../resources/stash-take.py?key=${token}`);
const json = await response.json();
return json;
}
// A helper to assert the existence of `token` that should have been stored in
// the server by fetching ../resources/stash-put.py.
//
// This function simply wait for a custom amount of time before trying to
// retrieve `token` from the server.
// `expectTokenExist` tells if `token` should be present or not.
//
// NOTE:
// In order to parallelize the work, we are going to have an async_test
// for the rest of the work. Note that we want the serialized behavior
// for the steps so far, so we don't want to make the entire test case
// an async_test.
function assertStashedTokenAsync(
testName, token, {expectTokenExist = true} = {}) {
async_test(test => {
new Promise(resolve => test.step_timeout(resolve, 3000 /*ms*/))
.then(test.step_func(() => {
return queryToken(token);
}))
.then(test.step_func(result => {
if (expectTokenExist) {
assert_equals(result, 'on', `token should be on (stashed).`);
test.done();
} else {
assert_not_equals(
result, 'on', `token should not be on (stashed).`);
return Promise.reject(`Failed to retrieve token from server`);
}
}))
.catch(test.step_func(e => {
if (expectTokenExist) {
test.unreached_func(e);
} else {
test.done();
}
}));
}, testName);
}
/**
* In an iframe, and in `load` event handler, test to fetch a keepalive URL that
* involves in redirect to another URL.
*
* `unloadIframe` to unload the iframe before verifying stashed token to
* simulate the situation that unloads after fetching. Note that this test is
* different from `keepaliveRedirectInUnloadTest()` in that the latter
* performs fetch() call directly in `unload` event handler, while this test
* does it in `load`.
*/
function keepaliveRedirectTest(desc, {
origin1 = '',
origin2 = '',
withPreflight = false,
unloadIframe = false,
expectFetchSucceed = true,
} = {}) {
desc = `[keepalive][iframe][load] ${desc}` +
(unloadIframe ? ' [unload at end]' : '');
promise_test(async (test) => {
const tokenToStash = token();
const iframe = document.createElement('iframe');
iframe.src = getKeepAliveAndRedirectIframeUrl(
tokenToStash, origin1, origin2, withPreflight);
document.body.appendChild(iframe);
await iframeLoaded(iframe);
assert_equals(await getTokenFromMessage(), tokenToStash);
if (unloadIframe) {
iframe.remove();
}
assertStashedTokenAsync(
desc, tokenToStash, {expectTokenExist: expectFetchSucceed});
}, `${desc}; setting up`);
}
/**
* Opens a different site window, and in `unload` event handler, test to fetch
* a keepalive URL that involves in redirect to another URL.
*/
function keepaliveRedirectInUnloadTest(desc, {
origin1 = '',
origin2 = '',
url2 = '',
withPreflight = false,
expectFetchSucceed = true
} = {}) {
desc = `[keepalive][new window][unload] ${desc}`;
promise_test(async (test) => {
const targetUrl =
`${HTTP_NOTSAMESITE_ORIGIN}/fetch/api/resources/keepalive-redirect-window.html?` +
`origin1=${origin1}&` +
`origin2=${origin2}&` +
`url2=${url2}&` + (withPreflight ? `with-headers` : ``);
const w = window.open(targetUrl);
const token = await getTokenFromMessage();
w.close();
assertStashedTokenAsync(
desc, token, {expectTokenExist: expectFetchSucceed});
}, `${desc}; setting up`);
}
/**
* utility to create pending keepalive fetch requests
* The pending request state is achieved by ensuring the server (trickle.py) does not
* immediately respond to the fetch requests.
* The response delay is set as a url parameter.
*/
function createPendingKeepAliveRequest(delay, remote = false) {
// trickle.py is a script that can make a delayed response to the client request
const trickleRemoteURL = get_host_info().HTTPS_REMOTE_ORIGIN + '/fetch/api/resources/trickle.py?count=1&ms=';
const trickleLocalURL = get_host_info().HTTP_ORIGIN + '/fetch/api/resources/trickle.py?count=1&ms=';
url = remote ? trickleRemoteURL : trickleLocalURL;
const body = '*'.repeat(10);
return fetch(url + delay, { keepalive: true, body, method: 'POST' }).then(res => {
return res.text();
}).then(() => {
return new Promise(resolve => step_timeout(resolve, 1));
}).catch((error) => {
return Promise.reject(error);;
})
}
|